text:
package lc.kra.system.keyboard.event;

import java.util.EventListener;

public interface GlobalKeyListener extends EventListener {
    /**
     * Invoked when a key has been pressed.
     */
	public void keyPressed(GlobalKeyEvent event);

    /**
     * Invoked when a key has been released.
     */
	public void keyReleased(GlobalKeyEvent event);
}


text:
// colour.cpp

// includes

#include "colour.h"
#include "util.h"

// functions

// colour_is_ok()

bool colour_is_ok(int colour) {

   return colour == Black || colour == White;
}

// colour_is_white()

bool colour_is_white(int colour) {

   ASSERT(colour_is_ok(colour));

   return colour == White;
}

// colour_is_black()

bool colour_is_black(int colour) {

   ASSERT(colour_is_ok(colour));

   return colour == Black;
}

// colour_equal()

bool colour_equal(int colour_1, int colour_2) {

   ASSERT(colour_is_ok(colour_2));

   return (colour_1 & colour_2) != 0;
}

// colour_opp()

int colour_opp(int colour) {

   ASSERT(colour_is_ok(colour));

   return colour ^ (BlackFlag^WhiteFlag);
}

// end of colour.cpp

redpajama_set_name: RedPajamaGithub


text:
package com.ciaoshen.thinkinjava.chapter14.pets;
import java.util.*;
import java.lang.reflect.*;

public class Gerbil extends Rodent{
    public Gerbil(){super();}
    public Gerbil(String name){super(name);}
}


text:
package org.uiautomation.ios.server.services;

public interface TakeScreenshotService {

  public String getScreenshot();
}


text:
import moment from './computeds/moment';
import ago from './computeds/ago';
import duration from './computeds/duration';

export {
  moment,
  ago,
  duration
};


text:
import vtk
from vtk.util.misc import vtkGetDataRoot

# create planes
# Create the RenderWindow, Renderer
#
ren = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer( ren )

iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)

# create pipeline
#
pl3d = vtk.vtkPLOT3DReader()
pl3d.SetXYZFileName( vtkGetDataRoot() + '/Data/combxyz.bin' )
pl3d.SetQFileName( vtkGetDataRoot() + '/Data/combq.bin' )
pl3d.SetScalarFunctionNumber( 100 )
pl3d.SetVectorFunctionNumber( 202 )
pl3d.Update()

eg = vtk.vtkExtractGrid()
eg.SetInputConnection(pl3d.GetOutputPort())
eg.SetSampleRate(4,4,4)

gs = vtk.vtkGlyphSource2D()
gs.SetGlyphTypeToThickArrow()
gs.SetScale( 1 )
gs.FilledOff()
gs.CrossOff()

glyph = vtk.vtkGlyph3D()
glyph.SetInputConnection(eg.GetOutputPort())
glyph.SetSource(gs.GetOutput())
glyph.SetScaleFactor( 0.75 )

mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(glyph.GetOutputPort())

actor = vtk.vtkActor()
actor.SetMapper(mapper)

ren.AddActor(actor)

cam=ren.GetActiveCamera()
cam.SetClippingRange( 3.95297, 50 )
cam.SetFocalPoint( 8.88908, 0.595038, 29.3342 )
cam.SetPosition( -12.3332, 31.7479, 41.2387 )
cam.SetViewUp( 0.060772, -0.319905, 0.945498 )

renWin.Render()


text:
`Command`
=========

Description:
------------

In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate
all information needed to perform an action or trigger an event at a later time.
This information includes the method name, the object that owns the method and values for the method parameters.

Four terms always associated with the command pattern are command, receiver, invoker and client.
A command object knows about receiver and invokes a method of the receiver.
Values for parameters of the receiver method are stored in the command. The receiver then does the work.
An invoker object knows how to execute a command, and optionally does bookkeeping about the command execution.
The invoker does not know anything about a concrete command, it knows only about command interface.
Both an invoker object and several command objects are held by a client object.
The client decides which commands to execute at which points. To execute a command,
it passes the command object to the invoker object.

Using command objects makes it easier to construct general components that need to delegate, sequence or execute method calls at a time of their choosing without the need to know the class of the method or the method parameters. Using an invoker object allows bookkeeping about command executions to be conveniently performed, as well as implementing different modes for commands, which are managed by the invoker object, without the need for the client to be aware of the existence of bookkeeping or modes.

Implementation:
---------------

Consider a "simple" switch. In this example we configure the Switch with two commands: to turn the light on and to turn the light off.

A benefit of this particular implementation of the command pattern is that the switch can be used with any device, not just a light - the Switch in the following example turns a light on and off, but the Switch's constructor is able to accept any subclasses of Command for its two parameters. For example, you could configure the Switch to start an engine.

Files:
------

[CommandFactory](CommandFactory.ts)

[CommandInterface](CommandInterface.ts)

[LightOnCommand](LightOnCommand.ts)

[LightOffCommand](LightOffCommand.ts)

[Demo](CommandPatternDemo.spec.ts)

## Running tests

```bash
npm test
```


text:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.resourcemanager.network.implementation;

import com.azure.core.management.SubResource;
import com.azure.resourcemanager.network.models.ApplicationGateway;
import com.azure.resourcemanager.network.models.ApplicationGatewayIpConfiguration;
import com.azure.resourcemanager.network.models.Network;
import com.azure.resourcemanager.network.models.Subnet;
import com.azure.resourcemanager.network.fluent.models.ApplicationGatewayIpConfigurationInner;
import com.azure.resourcemanager.resources.fluentcore.arm.ResourceUtils;
import com.azure.resourcemanager.resources.fluentcore.arm.models.implementation.ChildResourceImpl;

/** Implementation for ApplicationGatewayIpConfiguration. */
class ApplicationGatewayIpConfigurationImpl
    extends ChildResourceImpl<ApplicationGatewayIpConfigurationInner, ApplicationGatewayImpl, ApplicationGateway>
    implements ApplicationGatewayIpConfiguration,
        ApplicationGatewayIpConfiguration.Definition<ApplicationGateway.DefinitionStages.WithCreate>,
        ApplicationGatewayIpConfiguration.UpdateDefinition<ApplicationGateway.Update>,
        ApplicationGatewayIpConfiguration.Update {

    ApplicationGatewayIpConfigurationImpl(ApplicationGatewayIpConfigurationInner inner, ApplicationGatewayImpl parent) {
        super(inner, parent);
    }

    // Getters
    @Override
    public String name() {
        return this.innerModel().name();
    }

    // Fluent setters

    @Override
    public ApplicationGatewayIpConfigurationImpl withExistingSubnet(Subnet subnet) {
        return this.withExistingSubnet(subnet.parent().id(), subnet.name());
    }

    @Override
    public ApplicationGatewayIpConfigurationImpl withExistingSubnet(Network network, String subnetName) {
        return this.withExistingSubnet(network.id(), subnetName);
    }

    @Override
    public ApplicationGatewayIpConfigurationImpl withExistingSubnet(String networkId, String subnetName) {
        SubResource subnetRef = new SubResource().withId(networkId + "/subnets/" + subnetName);
        this.innerModel().withSubnet(subnetRef);
        return this;
    }

    // Verbs

    @Override
    public ApplicationGatewayImpl attach() {
        return this.parent().withConfig(this);
    }

    @Override
    public String networkId() {
        SubResource subnetRef = this.innerModel().subnet();
        if (subnetRef != null) {
            return ResourceUtils.parentResourceIdFromResourceId(subnetRef.id());
        } else {
            return null;
        }
    }

    @Override
    public String subnetName() {
        SubResource subnetRef = this.innerModel().subnet();
        if (subnetRef != null) {
            return ResourceUtils.nameFromResourceId(subnetRef.id());
        } else {
            return null;
        }
    }

    @Override
    public Subnet getSubnet() {
        return Utils.getAssociatedSubnet(this.parent().manager(), this.innerModel().subnet());
    }
}


text:
package org.apache.kylin.source.hive;

import static org.junit.Assert.assertTrue;

import org.apache.kylin.common.util.HBaseMetadataTestCase;
import org.apache.kylin.common.util.Pair;
import org.apache.kylin.metadata.model.TableDesc;
import org.apache.kylin.metadata.model.TableExtDesc;
import org.apache.kylin.source.ISource;
import org.apache.kylin.source.ISourceMetadataExplorer;
import org.apache.kylin.source.SourceManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class ITHiveSourceTableLoaderTest extends HBaseMetadataTestCase {

    @Before
    public void setup() throws Exception {
        super.createTestMetadata();
    }

    @After
    public void after() throws Exception {
        super.cleanupTestMetadata();
    }

    @Test
    public void test() throws Exception {
        ISource source = SourceManager.getDefaultSource();
        ISourceMetadataExplorer explr = source.getSourceMetadataExplorer();
        Pair<TableDesc, TableExtDesc> pair;

        pair = explr.loadTableMetadata("DEFAULT", "TEST_KYLIN_FACT", "default");
        assertTrue(pair.getFirst().getIdentity().equals("DEFAULT.TEST_KYLIN_FACT"));

        pair = explr.loadTableMetadata("EDW", "TEST_CAL_DT", "default");
        assertTrue(pair.getFirst().getIdentity().equals("EDW.TEST_CAL_DT"));

    }

}


text:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_auth"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.xt.android.rant.AuthActivity">

</FrameLayout>


text:
ACCEPTED

#### According to
International Plant Names Index

#### Published in
Prodr. 5:571. 1836

#### Original name
null

### Remarks
null


text:
package com.codingchili.webshoppe.model;

import com.codingchili.webshoppe.model.exception.ProductStoreException;

import java.util.List;

/**
 * Created by Robin on 2015-09-30.
 *
 * Interface to a product store.
 */

interface ProductStore {
    /**
     * Lists all available categories in the product store.
     * @return the names as strings.
     * @throws ProductStoreException when the store query has failed.
     */
    List<Category> listCategories() throws ProductStoreException;

    /**
     * Retrieves all products from the store matching the name-pattern.
     * @param nameFilter matches product names partially.
     * @return all products mathching the nameFilter.
     * @throws ProductStoreException when the store query has failed.
     */
    List<Product> listProductsByName(String nameFilter) throws ProductStoreException;

    List<Integer> listProductImages(Product product) throws ProductStoreException;

    /**
     * Retrieves all products from a specified category.
     * @param category to match.
     * @return all products matching the categoryFilter.
     * @throws ProductStoreException when the store query has failed.
     */
    List<Product> listProductsByCategory(Category category) throws ProductStoreException;

    /**
     * Get a product from the store by its id.
     * @param id of the product to be retrieved.
     * @return a product matching the id.
     * @throws ProductStoreException when the store query failed.
     */
    Product getProductById(int id) throws ProductStoreException;

    /**
     * Updates the information of an existing product.
     * @param id of the product to be updated.
     * @param cost of the product.
     * @param quantityModifier change in stock.
     * @param name the name of the product.
     * @param description the description text for the product.
     */
    void updateProduct(int id, int cost, int quantityModifier, String name, String description) throws ProductStoreException;

    /**
     * Adds a product to the store.
     * @param product to be added to the store.
     * @param categoryId id of the product to be added.
     */
    void addProduct(Product product, int categoryId) throws ProductStoreException;

    /**
     * Adds a category to the store.
     * @param category name of the category to be added.
     */
    void addCategory(String category) throws ProductStoreException;

    /**
     * Set the image of a product.
     * @param productId id of the product to change image.
     * @param image base64 encoded image data.
     */
    void setProductImage(int productId, String image) throws ProductStoreException;

    /**
     * Get the image of a product.
     * @param imageId the image to retrieve.
     * @return the image of a product.
     */
    Image getProductImage(int imageId) throws ProductStoreException;
}


text:
using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Grpc.Testing {

  /// Holder for reflection information generated from src/proto/grpc/testing/services.proto
  [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
  public static partial class ServicesReflection {

    #region Descriptor
    /// File descriptor for src/proto/grpc/testing/services.proto
    public static pbr::FileDescriptor Descriptor {
      get { return descriptor; }
    }
    private static pbr::FileDescriptor descriptor;

    static ServicesReflection() {
      byte[] descriptorData = global::System.Convert.FromBase64String(
          string.Concat(
            "CiVzcmMvcHJvdG8vZ3JwYy90ZXN0aW5nL3NlcnZpY2VzLnByb3RvEgxncnBj",
            "LnRlc3RpbmcaJXNyYy9wcm90by9ncnBjL3Rlc3RpbmcvbWVzc2FnZXMucHJv",
            "dG8aJHNyYy9wcm90by9ncnBjL3Rlc3RpbmcvY29udHJvbC5wcm90bzKqAQoQ",
            "QmVuY2htYXJrU2VydmljZRJGCglVbmFyeUNhbGwSGy5ncnBjLnRlc3Rpbmcu",
            "U2ltcGxlUmVxdWVzdBocLmdycGMudGVzdGluZy5TaW1wbGVSZXNwb25zZRJO",
            "Cg1TdHJlYW1pbmdDYWxsEhsuZ3JwYy50ZXN0aW5nLlNpbXBsZVJlcXVlc3Qa",
            "HC5ncnBjLnRlc3RpbmcuU2ltcGxlUmVzcG9uc2UoATABMpcCCg1Xb3JrZXJT",
            "ZXJ2aWNlEkUKCVJ1blNlcnZlchIYLmdycGMudGVzdGluZy5TZXJ2ZXJBcmdz",
            "GhouZ3JwYy50ZXN0aW5nLlNlcnZlclN0YXR1cygBMAESRQoJUnVuQ2xpZW50",
            "EhguZ3JwYy50ZXN0aW5nLkNsaWVudEFyZ3MaGi5ncnBjLnRlc3RpbmcuQ2xp",
            "ZW50U3RhdHVzKAEwARJCCglDb3JlQ291bnQSGS5ncnBjLnRlc3RpbmcuQ29y",
            "ZVJlcXVlc3QaGi5ncnBjLnRlc3RpbmcuQ29yZVJlc3BvbnNlEjQKClF1aXRX",
            "b3JrZXISEi5ncnBjLnRlc3RpbmcuVm9pZBoSLmdycGMudGVzdGluZy5Wb2lk",
            "YgZwcm90bzM="));
      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
          new pbr::FileDescriptor[] { global::Grpc.Testing.MessagesReflection.Descriptor, global::Grpc.Testing.ControlReflection.Descriptor, },
          new pbr::GeneratedCodeInfo(null, null));
    }
    #endregion

  }
}

#endregion Designer generated code


text:
module.exports = (GT, { defineMatcher }) => {
  const $RIGHT_HAND_EXPRESSION = defineMatcher('RightHandExpressionStatement', (ParentClass) => {
    return class ExpressionMatcher extends ParentClass {
      constructor(_opts) {
        var opts = _opts || {};

        super(opts);

        const {
          $SELECT,
          $LOOP,
          $FUNCTION_DECLARATOR,
          $EXPRESSION_STATEMENT
        } = GT;

        this.setMatcher(
          $LOOP(
            $SELECT(
              $FUNCTION_DECLARATOR(),
              $EXPRESSION_STATEMENT(),
              { debugSkip: true }
            ),
            this.getMatcherOptions({
              debugSkip: true,
              before: ({ context, token }) => {
                context.parentScope = token;
                return token;
              }
            })
          )
        );
      }

      respond(context) {
        var result = super.respond(context);
        if (!this.isToken(result) || this.isSkipToken(result))
          return result;

        var token = result;
        if (token.children.length > 1)
          return token;

        return token.children[0];
      }
    };
  });

  return {
    $RIGHT_HAND_EXPRESSION
  };
};


text:
class Order < ActiveRecord::Base
  validate :order_paid, before: [:ship]
  validates :number, presence: true
  validate :order_status_is_created, on: :create

  def ship
    update_column(:status, 'shipped')
  end

  def order_paid
    errors.add(:base, 'not paid')
  end

  def order_status_is_created
    if status != 'created'
      errors.add(:status, 'status should be created')
    end
  end
end


text:
@implementation ASPOSESDocumentResponse

+ (JSONKeyMapper*)keyMapper {
    NSDictionary *map = @{@"Document" : @"document",
                          @"Code" : @"code",
                          @"Status" : @"status",
                          @"Message" : @"message",
                          @"Matches" : @"matches"};

    return [[JSONKeyMapper alloc] initWithDictionary:map];
}

+(BOOL)propertyIsOptional:(NSString*)propertyName
{
    if([propertyName isEqualToString:@"matches"]) {
        return YES;
    }
    return NO;
}

@end


text:
<?php
namespace Qiniu\Storage;

final class Uploader
{

}


text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using winusbdotnet;

namespace winusbdotnet.UsbDevices
{
    public class RgbButton
    {
        public static IEnumerable<WinUSBEnumeratedDevice> Enumerate()
        {
            return WinUSBDevice.EnumerateDevices(new Guid("60d2a26c-1d22-4538-b7d5-337b2f07fff1"));
        }

        const byte OUT_PIPE = 0x03;
        const byte IN_PIPE = 0x83;
        const int ButtonThreshold = 0x7C;

        WinUSBDevice BaseDevice;

        /// <summary>
        /// Set of colors to display on the device.
        /// Modifying this array does not update the colors on the device, call SendButtonColors when ready to update.
        /// </summary>
        public RGBColor[] ButtonColors;

        /// <summary>
        /// Raw data from the device's button sensors in the range 0-127
        /// This value is an analog pressure value per button, lower values indicate higher pressure
        /// Technically, it is the log of a measurement of the resistance of the button.
        /// Typical values are 127 (unpressed), 0 (fully pressed), and most often a range of values around 40-80 when it's pressed lightly.
        /// If you push the button straight down, it's likely to hit 0, but if you activate it by pushing sideways, it typically ends up around 60.
        /// It's possible to express almost the full range, though there's very low resolution below about 40.
        /// </summary>
        public int[] ButtonValues;

        /// <summary>
        /// For each button, whether the button is currently being pressed down.
        /// </summary>
        public bool[] ButtonPressed;

        /// <summary>
        /// Count of the number of button data frames received from the device.
        /// </summary>
        public long DataCount;

        public RgbButton(WinUSBEnumeratedDevice dev)
        {
            BaseDevice = new WinUSBDevice(dev);

            ButtonColors = new RGBColor[4];
            ButtonValues = new int[4];
            ButtonPressed = new bool[4];

            BaseDevice.EnableBufferedRead(IN_PIPE);
            BaseDevice.BufferedReadNotifyPipe(IN_PIPE, NewDataCallback);
            EnableDisplayTimeout();
            EnableButtonData();
        }

        public void Close()
        {
            BaseDevice.Close();
            BaseDevice = null;
        }

        void NewDataCallback()
        {
            lock (this) // Prevent concurrent execution
            {
                bool newData = false;
                bool badData;

                while (BaseDevice.BufferedByteCountPipe(IN_PIPE) >= 5)
                {
                    badData = false;
                    byte[] data = BaseDevice.BufferedPeekPipe(IN_PIPE, 5);
                    if(data[0] != 0xFF)
                    {
                        badData = true;
                    }
                    for (int i = 0; i < 4; i++)
                    {
                        if(data[i + 1] >= 0x80)
                        {
                            // This is also a bad data (truncated) message, this can happen if the host falls behind.
                            badData = true;
                            break;
                        }
                    }
                    if(badData)
                    {
                        // Bad data. Try again next byte.
                        BaseDevice.BufferedSkipBytesPipe(IN_PIPE, 1);
                        continue;
                    }

                    // This looks like a button message
                    for(int i=0;i<4;i++)
                    {
                        ButtonValues[i] = data[i + 1];
                        ButtonPressed[i] = ButtonValues[i] < ButtonThreshold;
                    }
                    newData = true;
                    DataCount++;
                    BaseDevice.BufferedSkipBytesPipe(IN_PIPE, 5);
                }

                if(newData)
                {
                    // Provide notification.
                }
            }
        }

        /// <summary>
        /// Send a command to display the current value of ButtonColors across the button LEDs.
        /// This should be called after preparing a new set of ButtonColors to display.
        /// By default, if SendButtonColors is not called at least once every 5 seconds, the LEDs will timeout and turn off.
        /// [Enable|Disable]DisplayTimeout functions can control this behavior.
        /// </summary>
        public void SendButtonColors()
        {
            byte[] command = new byte[13];
            command[0] = (byte)'L';

            int n = 1;
            foreach(RGBColor c in ButtonColors)
            {
                command[n++] = c.RByte;
                command[n++] = c.GByte;
                command[n++] = c.BByte;
            }
            BaseDevice.WritePipe(OUT_PIPE, command);
        }

        void SendByteCommand(byte b)
        {
            byte[] command = new byte[1];
            command[0] = b;
            BaseDevice.WritePipe(OUT_PIPE, command);
        }

        /// <summary>
        /// Tell the device to start providing periodic updates on button status (>100Hz)
        /// </summary>
        public void EnableButtonData()
        {
            SendByteCommand((byte)'B');
        }

        /// <summary>
        /// Tell the device to stop providing button data, cached view in this class will become stale.
        /// </summary>
        public void DisableButtonData()
        {
            SendByteCommand((byte)'X');
        }

        /// <summary>
        /// Tell device to go blank if more than 5 seconds pass without an LED update (SendButtonColors)
        /// </summary>
        public void EnableDisplayTimeout()
        {
            SendByteCommand((byte)'T');
        }

        /// <summary>
        /// Tell device to not automatically blank, and always show the most recently sent button colors.
        /// </summary>
        public void DisableDisplayTimeout()
        {
            SendByteCommand((byte)'I');
        }

        /// <summary>
        /// Restart the device in a USB mass storage mode where it is possible to replace the firmware.
        /// Device will not respond to RgbButton commands in this mode.
        /// </summary>
        public void EnterProgrammingMode()
        {
            SendByteCommand((byte)'P');
        }

    }
}


text:
require "spec_helper"

describe Anaximander::Discovery::Assets do
  let(:html)   { Nokogiri::HTML(open("spec/data/page.html")) }
  let(:assets) { described_class.new(html) }

  # See `spec/data/page.html` for the HTML used in this test.

  it "collects all CSS and Javascript assets" do
    expect(assets.to_a).to eq(["main.css", "other.css", "allthethings.js"])
  end
end


text:
namespace fileapi {

namespace {
const base::FilePath::CharType kFileSystemDirName[] =
    FILE_PATH_LITERAL("File System");
const base::FilePath::CharType kOriginDatabaseName[] = FILE_PATH_LITERAL("Origins");
}  // namespace

TEST(FileSystemOriginDatabaseTest, BasicTest) {
  base::ScopedTempDir dir;
  ASSERT_TRUE(dir.CreateUniqueTempDir());
  const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
  EXPECT_FALSE(file_util::PathExists(kFSDir));
  EXPECT_TRUE(file_util::CreateDirectory(kFSDir));

  FileSystemOriginDatabase database(kFSDir);
  std::string origin("origin");

  EXPECT_FALSE(database.HasOriginPath(origin));
  // Double-check to make sure that had no side effects.
  EXPECT_FALSE(database.HasOriginPath(origin));

  base::FilePath path0;
  base::FilePath path1;

  // Empty strings aren't valid origins.
  EXPECT_FALSE(database.GetPathForOrigin(std::string(), &path0));

  EXPECT_TRUE(database.GetPathForOrigin(origin, &path0));
  EXPECT_TRUE(database.HasOriginPath(origin));
  EXPECT_TRUE(database.GetPathForOrigin(origin, &path1));
  EXPECT_FALSE(path0.empty());
  EXPECT_FALSE(path1.empty());
  EXPECT_EQ(path0, path1);

  EXPECT_TRUE(file_util::PathExists(kFSDir.Append(kOriginDatabaseName)));
}

TEST(FileSystemOriginDatabaseTest, TwoPathTest) {
  base::ScopedTempDir dir;
  ASSERT_TRUE(dir.CreateUniqueTempDir());
  const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
  EXPECT_FALSE(file_util::PathExists(kFSDir));
  EXPECT_TRUE(file_util::CreateDirectory(kFSDir));

  FileSystemOriginDatabase database(kFSDir);
  std::string origin0("origin0");
  std::string origin1("origin1");

  EXPECT_FALSE(database.HasOriginPath(origin0));
  EXPECT_FALSE(database.HasOriginPath(origin1));

  base::FilePath path0;
  base::FilePath path1;
  EXPECT_TRUE(database.GetPathForOrigin(origin0, &path0));
  EXPECT_TRUE(database.HasOriginPath(origin0));
  EXPECT_FALSE(database.HasOriginPath(origin1));
  EXPECT_TRUE(database.GetPathForOrigin(origin1, &path1));
  EXPECT_TRUE(database.HasOriginPath(origin1));
  EXPECT_FALSE(path0.empty());
  EXPECT_FALSE(path1.empty());
  EXPECT_NE(path0, path1);

  EXPECT_TRUE(file_util::PathExists(kFSDir.Append(kOriginDatabaseName)));
}

TEST(FileSystemOriginDatabaseTest, DropDatabaseTest) {
  base::ScopedTempDir dir;
  ASSERT_TRUE(dir.CreateUniqueTempDir());
  const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
  EXPECT_FALSE(file_util::PathExists(kFSDir));
  EXPECT_TRUE(file_util::CreateDirectory(kFSDir));

  FileSystemOriginDatabase database(kFSDir);
  std::string origin("origin");

  EXPECT_FALSE(database.HasOriginPath(origin));

  base::FilePath path0;
  EXPECT_TRUE(database.GetPathForOrigin(origin, &path0));
  EXPECT_TRUE(database.HasOriginPath(origin));
  EXPECT_FALSE(path0.empty());

  EXPECT_TRUE(file_util::PathExists(kFSDir.Append(kOriginDatabaseName)));

  database.DropDatabase();

  base::FilePath path1;
  EXPECT_TRUE(database.HasOriginPath(origin));
  EXPECT_TRUE(database.GetPathForOrigin(origin, &path1));
  EXPECT_FALSE(path1.empty());
  EXPECT_EQ(path0, path1);
}

TEST(FileSystemOriginDatabaseTest, DeleteOriginTest) {
  base::ScopedTempDir dir;
  ASSERT_TRUE(dir.CreateUniqueTempDir());
  const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
  EXPECT_FALSE(file_util::PathExists(kFSDir));
  EXPECT_TRUE(file_util::CreateDirectory(kFSDir));

  FileSystemOriginDatabase database(kFSDir);
  std::string origin("origin");

  EXPECT_FALSE(database.HasOriginPath(origin));
  EXPECT_TRUE(database.RemovePathForOrigin(origin));

  base::FilePath path0;
  EXPECT_TRUE(database.GetPathForOrigin(origin, &path0));
  EXPECT_TRUE(database.HasOriginPath(origin));
  EXPECT_FALSE(path0.empty());

  EXPECT_TRUE(database.RemovePathForOrigin(origin));
  EXPECT_FALSE(database.HasOriginPath(origin));

  base::FilePath path1;
  EXPECT_TRUE(database.GetPathForOrigin(origin, &path1));
  EXPECT_FALSE(path1.empty());
  EXPECT_NE(path0, path1);
}

TEST(FileSystemOriginDatabaseTest, ListOriginsTest) {
  base::ScopedTempDir dir;
  ASSERT_TRUE(dir.CreateUniqueTempDir());
  const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
  EXPECT_FALSE(file_util::PathExists(kFSDir));
  EXPECT_TRUE(file_util::CreateDirectory(kFSDir));

  std::vector<FileSystemOriginDatabase::OriginRecord> origins;

  FileSystemOriginDatabase database(kFSDir);
  EXPECT_TRUE(database.ListAllOrigins(&origins));
  EXPECT_TRUE(origins.empty());
  origins.clear();

  std::string origin0("origin0");
  std::string origin1("origin1");

  EXPECT_FALSE(database.HasOriginPath(origin0));
  EXPECT_FALSE(database.HasOriginPath(origin1));

  base::FilePath path0;
  base::FilePath path1;
  EXPECT_TRUE(database.GetPathForOrigin(origin0, &path0));
  EXPECT_TRUE(database.ListAllOrigins(&origins));
  EXPECT_EQ(origins.size(), 1UL);
  EXPECT_EQ(origins[0].origin, origin0);
  EXPECT_EQ(origins[0].path, path0);
  origins.clear();
  EXPECT_TRUE(database.GetPathForOrigin(origin1, &path1));
  EXPECT_TRUE(database.ListAllOrigins(&origins));
  EXPECT_EQ(origins.size(), 2UL);
  if (origins[0].origin == origin0) {
    EXPECT_EQ(origins[0].path, path0);
    EXPECT_EQ(origins[1].origin, origin1);
    EXPECT_EQ(origins[1].path, path1);
  } else {
    EXPECT_EQ(origins[0].origin, origin1);
    EXPECT_EQ(origins[0].path, path1);
    EXPECT_EQ(origins[1].origin, origin0);
    EXPECT_EQ(origins[1].path, path0);
  }
}

TEST(FileSystemOriginDatabaseTest, DatabaseRecoveryTest) {
  // Checks if FileSystemOriginDatabase properly handles database corruption.
  // In this test, we'll register some origins to the origin database, then
  // corrupt database and its log file.
  // After repairing, the origin database should be consistent even when some
  // entries lost.

  base::ScopedTempDir dir;
  ASSERT_TRUE(dir.CreateUniqueTempDir());
  const base::FilePath kFSDir = dir.path().Append(kFileSystemDirName);
  const base::FilePath kDBDir = kFSDir.Append(kOriginDatabaseName);
  EXPECT_FALSE(file_util::PathExists(kFSDir));
  EXPECT_TRUE(file_util::CreateDirectory(kFSDir));

  const std::string kOrigins[] = {
    "foo.example.com",
    "bar.example.com",
    "baz.example.com",
    "hoge.example.com",
    "fuga.example.com",
  };

  scoped_ptr<FileSystemOriginDatabase> database(
      new FileSystemOriginDatabase(kFSDir));
  for (size_t i = 0; i < arraysize(kOrigins); ++i) {
    base::FilePath path;
    EXPECT_FALSE(database->HasOriginPath(kOrigins[i]));
    EXPECT_TRUE(database->GetPathForOrigin(kOrigins[i], &path));
    EXPECT_FALSE(path.empty());
    EXPECT_TRUE(database->GetPathForOrigin(kOrigins[i], &path));

    if (i != 1)
      EXPECT_TRUE(file_util::CreateDirectory(kFSDir.Append(path)));
  }
  database.reset();

  const base::FilePath kGarbageDir = kFSDir.AppendASCII("foo");
  const base::FilePath kGarbageFile = kGarbageDir.AppendASCII("bar");
  EXPECT_TRUE(file_util::CreateDirectory(kGarbageDir));
  bool created = false;
  base::PlatformFileError error;
  base::PlatformFile file = base::CreatePlatformFile(
      kGarbageFile,
      base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
      &created, &error);
  EXPECT_EQ(base::PLATFORM_FILE_OK, error);
  EXPECT_TRUE(created);
  EXPECT_TRUE(base::ClosePlatformFile(file));

  // Corrupt database itself and last log entry to drop last 1 database
  // operation.  The database should detect the corruption and should recover
  // its consistency after recovery.
  CorruptDatabase(kDBDir, leveldb::kDescriptorFile,
                  0, std::numeric_limits<size_t>::max());
  CorruptDatabase(kDBDir, leveldb::kLogFile, -1, 1);

  base::FilePath path;
  database.reset(new FileSystemOriginDatabase(kFSDir));
  std::vector<FileSystemOriginDatabase::OriginRecord> origins_in_db;
  EXPECT_TRUE(database->ListAllOrigins(&origins_in_db));

  // Expect all but last added origin will be repaired back, and kOrigins[1]
  // should be dropped due to absence of backing directory.
  EXPECT_EQ(arraysize(kOrigins) - 2, origins_in_db.size());

  const std::string kOrigin("piyo.example.org");
  EXPECT_FALSE(database->HasOriginPath(kOrigin));
  EXPECT_TRUE(database->GetPathForOrigin(kOrigin, &path));
  EXPECT_FALSE(path.empty());
  EXPECT_TRUE(database->HasOriginPath(kOrigin));

  EXPECT_FALSE(file_util::PathExists(kGarbageFile));
  EXPECT_FALSE(file_util::PathExists(kGarbageDir));
}

}  // namespace fileapi


text:
type Callback<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn;

/**
 * Returns a deprecation message or null to skip the deprecation.
 */
type Handler<TArgs extends unknown[]> = Callback<TArgs, string | null>;
type GlobalAccessHandler = Handler<[]>;
type DotAccessHandler = Handler<[dotKey: string, importKey: string, module: string]>;

export let onEmberGlobalAccess: GlobalAccessHandler | undefined;
export let onComputedDotAccess: DotAccessHandler | undefined;
export let onRunloopDotAccess: DotAccessHandler | undefined;


text:
v3.0.0 - May 17, 2017

* 6b56bfe Fix: Use correct starting range and loc for JSXText tokens (fixes #227) (#271) (Reyad Attiyat)
* f5fcc87 Breaking: Allow comment scanner to rescan tokens (fixes #216) (#219) (Reyad Attiyat)
* f836bb9 Chore: Refactor the codebase (fixes #220) (#261) (James Henry)
* aade6bd Chore: Update README with list of known issues (#247) (Reyad Attiyat)
* c8e881a Breaking: Normalize type parameters (fixes #197) (#196) (Rasmus Eneman)
* d37bf04 Fix: Type parameter start location calculation (fixes #260) (#259) (Igor Oleinikov)
* 1a97650 Fix: Handle case where class has extends but no super class (fixes #249) (#254) (Reyad Attiyat)
* 00ad71d Fix: add `instanceof` to ast-converter (fixes #252) (#251) (Danny Arnold)
* 2989f8b Upgrade: Update semver package (#246) (Simen Bekkhus)
* b1efe69 Breaking: Change how interface node gets converted (fixes #201) (#241) (Reyad Attiyat)
* e311620 Fix: Set await property on async iterators (for await) (fixes #236) (#239) (Reyad Attiyat)
* a294afa Fix: Set async on async FunctionExpressions (fixes #244) (#245) (Lucas Azzola)
* 7c00f16 Chore: Add tests for object spread and async generator (refs #236) (#237) (Reyad Attiyat)
* 7b69bc9 Fix: Label abstract class properties (fixes #234) (#238) (Reyad Attiyat)
* a330ec6 New: Add support for default type parameters (fixes #235) (#240) (Reyad Attiyat)
* e1ef800 Fix: Support superTypeParameters (fixes #242) (#243) (Lucas Azzola)
* 65c2e0a Breaking: Support TypeScript 2.3 (fixes #232) (#233) (Lucas Azzola)
* 15f1173 Fix: Use TSAbsractMethodDefinition for abstract constructor (fixes #228) (#229) (Lucas Azzola)
* 8fb71d2 Breaking: Add .body to TSModuleBlock nodes (fixes #217) (#218) (Philipp A)
* 471f403 Chore: Remove before_script from .travis.yml (fixes #231) (#230) (James Henry)
* 9397c5c Chore: Cleanup Makefile (#221) (Reyad Attiyat)
* dd57f81 Update: Open TS peerDependency, warn non-supported version (fixes #167) (#193) (James Henry)
* a37d5ed Fix: Wrap any parameter with modifiers, not just in constructors (#214) (Rasmus Eneman)

v2.1.0 - April 4, 2017

* d709fd8 Fix: Set root to true in eslintrc (fixes #211) (#212) (Reyad Attiyat)
* 1e73711 Fix: Optional methods are not marked as optional (fixes #206) (#207) (Rasmus Eneman)
* 1cee2e3 Fix: Nested type arguments are not preserved (fixes #204) (#205) (Rasmus Eneman)
* 5a324a3 Fix: Preserve type parameters for methods (fixes #202) (#203) (Rasmus Eneman)
* bfb1506 New: Add type parameters to more AST nodes (fixes #184) (#183) (Rasmus Eneman)
* 0fadfc3 Fix: Convert MetaProperty (new.target) nodes correcly (fixes #194) (#195) (Reyad Attiyat)
* 4d755ed New: Store type parameter constraints (fixes #188) (#189) (Rasmus Eneman)
* 29d848c Fix: Updated broken class-with-optional-properties test result (#192) (James Henry)
* 04f6556 New: Mark optional parameters and properties (fixes #186) (#187) (Rasmus Eneman)
* cc9d4b3 Fix: Add missing typeAnnotation to class properties (fixes  #190) (#191) (Rasmus Eneman)
* 215a012 Fix: Change DoWhileStatement to DoStatement to match TS (fixes #180) (#181) (James Henry)
* 581a7a5 Fix: Missing parameter properties info in constructors (fixes #143) (#168) (patricio trevino)
* 69d2537 Chore: Add test for constructor and methods with parameters (refs #168) (#178) (Reyad Attiyat)
* c2a0b71 Fix: Add start and end property to tokens (fixes #172) (#176) (Reyad Attiyat)
* 2640d81 Fix: Remove jsdoc node property from ts nodes (fixes #164) (#177) (Reyad Attiyat)
* 701e2c5 Fix: Set name to type JSXIdentifier and fix selfClosing (fixes #172) (#175) (Reyad Attiyat)
* 8b4e548 Fix: Convert Void and Delete expressions to UnaryExpression (fixes #171) (#174) (Reyad Attiyat)

v2.0.0 - February 24, 2017

* 38aef53 Breaking: Updated supported TypeScript version to ~2.2.1 (fixes #149) (#169) (James Henry)
* 25207e0 Fix: Optimize convertTokens, treat JsxText as token (fixes #70) (#158) (James Henry)
* 76c33f8 Fix: Await node should have argument property (fixes #160) (#161) (Reyad Attiyat)
* 2f86bef Fix: Unescape identifiers typescript may prepend underscore (fixes #145) (#159) (Reyad Attiyat)

v1.0.3 - February 10, 2017

* 19e7f15 Docs: Show currently supported TypeScript version (#157) (James Henry)
* e96ba1f Fix: Calculate range correctly for exported generic class (fixes #152) (#155) (Reyad Attiyat)
* 11d5a7d Fix: Handle object types without annotations (fixes #148) (#154) (Reyad Attiyat)
* fc1e6bb Fix: Parameter with assignation provide type annotations (fixes #146) (#147) (patricio trevino)
* e5f378f Fix: Calculate range correctly when class is exported (fixes #152) (#153) (Reyad Attiyat)
* 6312383 Fix: Calculate constructor range using node.parameters.pos (fixes #139) (#140) (Reyad Attiyat)

v1.0.2 - January 12, 2017

* d53f1f8 Fix: Use ts utilities determine variable declaration type (fixes #136) (#138) (Reyad Attiyat)
* 918190d Fix: export type alias ExportNamedDeclaration node generation (fixes #134) (#135) (patricio trevino)

v1.0.1 - January 4, 2017

* 9882a8e Fix: Add missing async property (#133) (James Henry)
* 60843ad Fix: Handle async/await (fixes #119) (#129) (Philipp A)
* 0ff19dd Fix: Exception thrown when space occurs after function name (fixes #123) (#124) (Reyad Attiyat)
* ff283aa Fix: Allow running without options (fixes #121) (#120) (Philipp A)
* dd03b2f Docs: Changed --save to --save-dev in readme (#132) (Amila Welihinda)
* 41ccef5 Build: Add TS as dev-dep, only support minor range (#131) (James Henry)

v1.0.0 - November 11, 2016

* c60f216 Chore: Normalize .yml line endings (fixes #113) (#115) (James Henry)
* 9521396 Breaking: Updated to TypeScript 2.x (fixes #105) (#112) (James Henry)
* a7320df Docs: Update license copyright (Nicholas C. Zakas)
* 51ec64d Fix: Exclude TSNode.flags when deeplyCopy fallback is used (refs #105) (#107) (James Henry)
* 7ebf4d5 Fix: Rename decorator example to avoid issue in TS 2.x (refs #105) (#106) (James Henry)
* 45b9874 Fix: Added missing yml lf gitattribute (#104) (James Henry)
* 4dd3439 Build: Add Node 6.x to Travis (fixes #99) (#98) (Danny Fritz)

v0.4.0 - September 23, 2016

* eb1ad9b Fix: Add loc and range data to generated VariableDeclarator node (#100) (James Henry)
* 5dae849 New: Accessibility Modifiers (fixes #87) (#88) (Danny Fritz)
* 68992eb Fix: Tests break with Windows line-endings (fixes #93) (#97) (Danny Fritz)

v0.3.1 - September 20, 2016

* e36d800 Fix: Convert TypeAliasDeclaration into VariableDeclarator (fixes #89) (#91) (James Henry)
* ac0c95d Fix: Prefix function declarations in TS namespaces (fixes #78) (#82) (James Henry)
* 7cc865e Fix: Support abstract classes and methods (fixes #80) (#81) (James Henry)

v0.3.0 - September 13, 2016

* e76f3b9 New: Convert type alias into variable declaration (refs #77) (#83) (James Henry)
* 5c47ad5 Fix: Convert TSTypeOfExpression to UnaryExpression (fixes #85) (#86) (James Henry)
* 799fd63 Fix: Distinguish between DeclareFunction and FunctionDeclaration (#79) (James Henry)

v0.2.0 - August 29, 2016

* 138495f New: Add accessor decorators to AST (fixes #63) (#73) (James Henry)
* f6a8e71 New: Add property decorators to AST (fixes #71) (#72) (James Henry)
* 328259f New: Add param decorators to the AST (fixes #68) (#69) (James Henry)
* 8b97fe7 New: Add class decorators to AST (fixes #66) (#67) (James Henry)
* 7364cb9 New: Add method decorators to AST (fixes #65) (#64) (James Henry)

v0.1.3 - August 22, 2016

* da984bf Fix: manually update package.json after build failure (Nicholas C. Zakas)
* 5bfb9bf Fix: Check for arguments property on NewExpression (fixes #60) (#62) (James Henry)
* d323ee7 Docs: Add issue template (Nicholas C. Zakas)
* 8f4964c Fix: UpdateExpression detection and operator format (fixes #58) (#59) (James Henry)
* e09ebb3 Fix: Add function type parameters (fixes #52) (#56) (James Henry)

v0.1.1 - August 10, 2016

* 62d14b4 Fix: Class implements generic syntax (fixes #44) (#53) (James Henry)

v0.1.0 - August 9, 2016

* d49b5f1 Build: Add CI build scripts (Nicholas C. Zakas)
* 32a46b3 New: Attaches comments to the ESTree AST (fixes #31) (#49) (James Henry)
* f4856f9 Fix: Class implements conversion (fixes #39) (#43) (Nicholas C. Zakas)
* 795a418 Build: Add linting to npm test (Nicholas C. Zakas)
* d26fec4 New: Automatically generate TS-specific nodes (#40) (Nicholas C. Zakas)
* 46b9c87 Docs: Update README to have the correct phase order. (#35) (Dean Taub)
* a8b00fe Chore: Add jQuery Foundation copyright (Nicholas C. Zakas)
* 560bb99 Fix: Remove console logging of node.kind (fixes #29) (Tom X. Tobin)

v0.1.0-alpha.1 - March 18, 2016

* 0eddb71 New: Implements JSX syntax (fixes #18) (James Henry)
* e890743 Docs: PR validation check (Nicholas C. Zakas)
* c314f77 Build: Don't test Node 0.10 (Nicholas C. Zakas)
* bc05e1a Fix: Ensure true, false and null values are not stringifed (fixes #1) (James Henry)

v0.1.0-alpha.0 - March 4, 2016

* c05b5da Build: Add release tool (Nicholas C. Zakas)
* 091973b Fix: SyntaxKind code checks, update TS peerDependency ^1.7.3 (fixes #15) (James Henry)
* e34f6b4 Fix: Use YAML file for ESLint config (Nicholas C. Zakas)
* e19b29d Update: TypeScript to be a peer dependency (fixes #12) (Nicholas C. Zakas)
* bdb1e3a Fix: Make tests actually work (fixes #6) (Nicholas C. Zakas)
* c61cc81 Docs: Improved detail and accuracy of JSDoc blocks (James Henry)
* 918a9cc Fix: Fixes linting errors (fixes #10) (James Henry)
* 1721dde Fix: Bumped lint script dependencies (fixes #8) (James Henry)
* 2f04e92 Fix: Removed unused reference to acorn-jsx/inject (fixes #2) (James Henry)
* 0e9d61e Docs: Update README with plans (Nicholas C. Zakas)
* c88e3ab New: Finished up ES6 features (Nicholas C. Zakas)
* 0ea1e32 Build: Tag as alpha release (Nicholas C. Zakas)
* 08702b8 Docs: Update README (Nicholas C. Zakas)
* c88ba98 New: First commit (Nicholas C. Zakas)


text:
#if defined(ENABLE_D3D12)

#include <algorithm>
#include <stdexcept>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/euler_angles.hpp>

#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>

#include "common/mesh.hpp"
#include "common/image.hpp"

#include <d3dx12.h>
#include <d3dcompiler.h>
#include "d3d12.hpp"

namespace D3D12 {

struct TransformCB
{
	glm::mat4 viewProjectionMatrix;
	glm::mat4 skyProjectionMatrix;
	glm::mat4 sceneRotationMatrix;
};

struct ShadingCB
{
	struct {
		glm::vec4 direction;
		glm::vec4 radiance;
	} lights[SceneSettings::NumLights];
	glm::vec4 eyePosition;
};

GLFWwindow* Renderer::initialize(int width, int height, int maxSamples)
{
	glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
	GLFWwindow* window = glfwCreateWindow(width, height, "Physically Based Rendering (Direct3D 12)", nullptr, nullptr);
	if(!window) {
		throw std::runtime_error("Failed to create window");
	}

	UINT dxgiFactoryFlags = 0;

#ifdef _DEBUG
	{
		ComPtr<ID3D12Debug> debugController;
		if(SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) {
			debugController->EnableDebugLayer();
			dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;
		}
	}
#endif

	ComPtr<IDXGIFactory4> dxgiFactory;
	if(FAILED(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&dxgiFactory)))) {
		throw std::runtime_error("Failed to create DXGI factory");
	}

	// Find D3D12 compatible adapter and create the device object.
	std::string dxgiAdapterDescription;
	{
		ComPtr<IDXGIAdapter1> adapter = getAdapter(dxgiFactory);
		if(adapter) {
			DXGI_ADAPTER_DESC adapterDesc;
			adapter->GetDesc(&adapterDesc);
			dxgiAdapterDescription = Utility::convertToUTF8(adapterDesc.Description);
		}
		else {
			throw std::runtime_error("No suitable video adapter supporting D3D12 found");
		}

		if(FAILED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)))) {
			throw std::runtime_error("Failed to create D3D12 device");
		}

	}

	// Create default direct command queue.
	D3D12_COMMAND_QUEUE_DESC queueDesc = {};
	queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
	queueDesc.Type  = D3D12_COMMAND_LIST_TYPE_DIRECT;
	if(FAILED(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue)))) {
		throw std::runtime_error("Failed to create command queue");
	}

	// Create window swap chain.
	{
		DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
		swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
		swapChainDesc.SampleDesc.Count = 1;
		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		swapChainDesc.BufferCount = NumFrames;
		swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;

		ComPtr<IDXGISwapChain1> swapChain;
		if(FAILED(dxgiFactory->CreateSwapChainForHwnd(m_commandQueue.Get(), glfwGetWin32Window(window), &swapChainDesc, nullptr, nullptr, &swapChain))) {
			throw std::runtime_error("Failed to create swap chain");
		}
		swapChain.As(&m_swapChain);
	}
	m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
	dxgiFactory->MakeWindowAssociation(glfwGetWin32Window(window), DXGI_MWA_NO_ALT_ENTER);

	// Determine maximum supported MSAA level.
	UINT samples;
	for(samples = maxSamples; samples > 1; samples /= 2) {
		D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS mqlColor = { DXGI_FORMAT_R16G16B16A16_FLOAT, samples };
		D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS mqlDepthStencil = { DXGI_FORMAT_D24_UNORM_S8_UINT, samples };
		m_device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &mqlColor, sizeof(D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS));
		m_device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &mqlDepthStencil, sizeof(D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS));
		if(mqlColor.NumQualityLevels > 0 && mqlDepthStencil.NumQualityLevels > 0) {
			break;
		}
	}

	// Determine supported root signature version.
	{
		D3D12_FEATURE_DATA_ROOT_SIGNATURE rootSignatureFeature = {D3D_ROOT_SIGNATURE_VERSION_1_1};
		m_device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &rootSignatureFeature, sizeof(D3D12_FEATURE_DATA_ROOT_SIGNATURE));
		m_rootSignatureVersion = rootSignatureFeature.HighestVersion;
	}

	// Create descriptor heaps.
	m_descHeapRTV = createDescriptorHeap({D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 16, D3D12_DESCRIPTOR_HEAP_FLAG_NONE});
	m_descHeapDSV = createDescriptorHeap({D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 16, D3D12_DESCRIPTOR_HEAP_FLAG_NONE});
	m_descHeapCBV_SRV_UAV = createDescriptorHeap({D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 128, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE});

	// Create per-frame resources.
	for(UINT frameIndex=0; frameIndex < NumFrames; ++frameIndex) {

		if(FAILED(m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocators[frameIndex])))) {
			throw std::runtime_error("Failed to create command allocator");
		}
		if(FAILED(m_swapChain->GetBuffer(frameIndex, IID_PPV_ARGS(&m_backbuffers[frameIndex].buffer)))) {
			throw std::runtime_error("Failed to retrieve swap chain back buffer");
		}

		m_backbuffers[frameIndex].rtv = m_descHeapRTV.alloc();
		m_device->CreateRenderTargetView(m_backbuffers[frameIndex].buffer.Get(), nullptr, m_backbuffers[frameIndex].rtv.cpuHandle);

		m_framebuffers[frameIndex] = createFrameBuffer(width, height, samples, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_D24_UNORM_S8_UINT);
		if(samples > 1) {
			m_resolveFramebuffers[frameIndex] = createFrameBuffer(width, height, 1, DXGI_FORMAT_R16G16B16A16_FLOAT, (DXGI_FORMAT)0);
		}
		else {
			m_resolveFramebuffers[frameIndex] = m_framebuffers[frameIndex];
		}
	}

	// Create frame synchronization objects.
	{
		if(FAILED(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)))) {
			throw std::runtime_error("Failed to create fence object");
		}
		m_fenceCompletionEvent = CreateEvent(nullptr, false, false, nullptr);
	}

	std::printf("Direct3D 12 Renderer [%s]\n", dxgiAdapterDescription.c_str());
	return window;
}

void Renderer::shutdown()
{
	waitForGPU();
	CloseHandle(m_fenceCompletionEvent);
}

void Renderer::setup()
{
	CD3DX12_STATIC_SAMPLER_DESC defaultSamplerDesc{0, D3D12_FILTER_ANISOTROPIC};
	defaultSamplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

	CD3DX12_STATIC_SAMPLER_DESC computeSamplerDesc{0, D3D12_FILTER_MIN_MAG_MIP_LINEAR};
	defaultSamplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

	CD3DX12_STATIC_SAMPLER_DESC spBRDF_SamplerDesc{1, D3D12_FILTER_MIN_MAG_MIP_LINEAR};
	spBRDF_SamplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
	spBRDF_SamplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
	spBRDF_SamplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;

	// Create default command list.
	if(FAILED(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocators[0].Get(), nullptr, IID_PPV_ARGS(&m_commandList)))) {
		throw std::runtime_error("Failed to create direct command list");
	}

	// Create root signature & pipeline configuration for tonemapping.
	{
		ComPtr<ID3DBlob> tonemapVS = compileShader("shaders/hlsl/tonemap.hlsl", "main_vs", "vs_5_0");
		ComPtr<ID3DBlob> tonemapPS = compileShader("shaders/hlsl/tonemap.hlsl", "main_ps", "ps_5_0");

		const CD3DX12_DESCRIPTOR_RANGE1 descriptorRanges[] = {
			{D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE},
		};
		CD3DX12_ROOT_PARAMETER1 rootParameters[1];
		rootParameters[0].InitAsDescriptorTable(1, &descriptorRanges[0], D3D12_SHADER_VISIBILITY_PIXEL);
		CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC signatureDesc = {};
		signatureDesc.Init_1_1(1, rootParameters, 1, &computeSamplerDesc, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS);
		m_tonemapRootSignature = createRootSignature(signatureDesc);

		D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
		psoDesc.pRootSignature = m_tonemapRootSignature.Get();
		psoDesc.VS = CD3DX12_SHADER_BYTECODE(tonemapVS.Get());
		psoDesc.PS = CD3DX12_SHADER_BYTECODE(tonemapPS.Get());
		psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC{D3D12_DEFAULT};
		psoDesc.RasterizerState.FrontCounterClockwise = true;
		psoDesc.BlendState = CD3DX12_BLEND_DESC{D3D12_DEFAULT};
		psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
		psoDesc.NumRenderTargets = 1;
		psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
		psoDesc.SampleDesc.Count = 1;
		psoDesc.SampleMask = UINT_MAX;

		if(FAILED(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_tonemapPipelineState)))) {
			throw std::runtime_error("Failed to create tonemap pipeline state");
		}
	}

	// Create root signature & pipeline configuration for rendering PBR model.
	{
		const std::vector<D3D12_INPUT_ELEMENT_DESC> meshInputLayout = {
			{ "POSITION",  0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
			{ "NORMAL",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
			{ "TANGENT",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
			{ "BITANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 36, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD",  0, DXGI_FORMAT_R32G32_FLOAT,    0, 48, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
		};

		ComPtr<ID3DBlob> pbrVS = compileShader("shaders/hlsl/pbr.hlsl", "main_vs", "vs_5_0");
		ComPtr<ID3DBlob> pbrPS = compileShader("shaders/hlsl/pbr.hlsl", "main_ps", "ps_5_0");

		const CD3DX12_DESCRIPTOR_RANGE1 descriptorRanges[] = {
			{D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC},
			{D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 7, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC},
		};
		CD3DX12_ROOT_PARAMETER1 rootParameters[3];
		rootParameters[0].InitAsDescriptorTable(1, &descriptorRanges[0], D3D12_SHADER_VISIBILITY_VERTEX);
		rootParameters[1].InitAsDescriptorTable(1, &descriptorRanges[0], D3D12_SHADER_VISIBILITY_PIXEL);
		rootParameters[2].InitAsDescriptorTable(1, &descriptorRanges[1], D3D12_SHADER_VISIBILITY_PIXEL);
		D3D12_STATIC_SAMPLER_DESC staticSamplers[2];
		staticSamplers[0] = defaultSamplerDesc;
		staticSamplers[1] = spBRDF_SamplerDesc;
		CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC signatureDesc;
		signatureDesc.Init_1_1(3, rootParameters, 2, staticSamplers, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
		m_pbrRootSignature = createRootSignature(signatureDesc);

		D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
		psoDesc.pRootSignature = m_pbrRootSignature.Get();
		psoDesc.InputLayout = { meshInputLayout.data(), (UINT)meshInputLayout.size() };
		psoDesc.VS = CD3DX12_SHADER_BYTECODE(pbrVS.Get());
		psoDesc.PS = CD3DX12_SHADER_BYTECODE(pbrPS.Get());
		psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
		psoDesc.RasterizerState.FrontCounterClockwise = true;
		psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
		psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
		psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
		psoDesc.NumRenderTargets = 1;
		psoDesc.RTVFormats[0] = DXGI_FORMAT_R16G16B16A16_FLOAT;
		psoDesc.DSVFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
		psoDesc.SampleDesc.Count = m_framebuffers[0].samples;
		psoDesc.SampleMask = UINT_MAX;

		if(FAILED(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pbrPipelineState)))) {
			throw std::runtime_error("Failed to create graphics pipeline state for PBR model");
		}
	}

	// Load PBR model assets.
	m_pbrModel = createMeshBuffer(Mesh::fromFile("meshes/cerberus.fbx"));

	m_albedoTexture = createTexture(Image::fromFile("textures/cerberus_A.png"), DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
	m_normalTexture = createTexture(Image::fromFile("textures/cerberus_N.png"), DXGI_FORMAT_R8G8B8A8_UNORM);
	m_metalnessTexture = createTexture(Image::fromFile("textures/cerberus_M.png", 1), DXGI_FORMAT_R8_UNORM);
	m_roughnessTexture = createTexture(Image::fromFile("textures/cerberus_R.png", 1), DXGI_FORMAT_R8_UNORM);

	// Create root signature & pipeline configuration for rendering skybox.
	{
		const std::vector<D3D12_INPUT_ELEMENT_DESC> skyboxInputLayout = {
			{ "POSITION",  0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
		};

		ComPtr<ID3DBlob> skyboxVS = compileShader("shaders/hlsl/skybox.hlsl", "main_vs", "vs_5_0");
		ComPtr<ID3DBlob> skyboxPS = compileShader("shaders/hlsl/skybox.hlsl", "main_ps", "ps_5_0");

		const CD3DX12_DESCRIPTOR_RANGE1 descriptorRanges[] = {
			{D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC},
			{D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC},
		};
		CD3DX12_ROOT_PARAMETER1 rootParameters[2];
		rootParameters[0].InitAsDescriptorTable(1, &descriptorRanges[0], D3D12_SHADER_VISIBILITY_VERTEX);
		rootParameters[1].InitAsDescriptorTable(1, &descriptorRanges[1], D3D12_SHADER_VISIBILITY_PIXEL);
		CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC signatureDesc = {};
		signatureDesc.Init_1_1(2, rootParameters, 1, &defaultSamplerDesc, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
		m_skyboxRootSignature = createRootSignature(signatureDesc);

		D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
		psoDesc.pRootSignature = m_skyboxRootSignature.Get();
		psoDesc.InputLayout = { skyboxInputLayout.data(), (UINT)skyboxInputLayout.size() };
		psoDesc.VS = CD3DX12_SHADER_BYTECODE(skyboxVS.Get());
		psoDesc.PS = CD3DX12_SHADER_BYTECODE(skyboxPS.Get());
		psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
		psoDesc.RasterizerState.FrontCounterClockwise = true;
		psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
		psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
		psoDesc.NumRenderTargets = 1;
		psoDesc.RTVFormats[0] = DXGI_FORMAT_R16G16B16A16_FLOAT;
		psoDesc.SampleDesc.Count = m_framebuffers[0].samples;
		psoDesc.SampleMask = UINT_MAX;

		if(FAILED(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_skyboxPipelineState)))) {
			throw std::runtime_error("Failed to create graphics pipeline state for skybox");
		}
	}

	// Load skybox assets.
	m_skybox = createMeshBuffer(Mesh::fromFile("meshes/skybox.obj"));

	// Load & pre-process environment map.
	{
		ComPtr<ID3D12RootSignature> computeRootSignature;

		ID3D12DescriptorHeap* computeDescriptorHeaps[] = {
			m_descHeapCBV_SRV_UAV.heap.Get()
		};

		// Create universal compute root signature.
		{
			const CD3DX12_DESCRIPTOR_RANGE1 descriptorRanges[] = {
				{D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC},
				{D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE},
			};
			CD3DX12_ROOT_PARAMETER1 rootParameters[3];
			rootParameters[0].InitAsDescriptorTable(1, &descriptorRanges[0]);
			rootParameters[1].InitAsDescriptorTable(1, &descriptorRanges[1]);
			rootParameters[2].InitAsConstants(1, 0);
			CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC signatureDesc;
			signatureDesc.Init_1_1(3, rootParameters, 1, &computeSamplerDesc);
			computeRootSignature = createRootSignature(signatureDesc);
		}

		m_envTexture = createTexture(1024, 1024, 6, DXGI_FORMAT_R16G16B16A16_FLOAT);
		{
			DescriptorHeapMark mark(m_descHeapCBV_SRV_UAV);

			// Unfiltered environment cube map (temporary).
			Texture envTextureUnfiltered = createTexture(1024, 1024, 6, DXGI_FORMAT_R16G16B16A16_FLOAT);
			createTextureUAV(envTextureUnfiltered, 0);

			// Load & convert equirectangular environment map to cubemap texture
			{
				DescriptorHeapMark mark(m_descHeapCBV_SRV_UAV);

				Texture envTextureEquirect = createTexture(Image::fromFile("environment.hdr"), DXGI_FORMAT_R32G32B32A32_FLOAT, 1);

				ComPtr<ID3D12PipelineState> pipelineState;
				ComPtr<ID3DBlob> equirectToCubemapShader = compileShader("shaders/hlsl/equirect2cube.hlsl", "main", "cs_5_0");

				D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
				psoDesc.pRootSignature = computeRootSignature.Get();
				psoDesc.CS = CD3DX12_SHADER_BYTECODE(equirectToCubemapShader.Get());
				if(FAILED(m_device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&pipelineState)))) {
					throw std::runtime_error("Failed to create compute pipeline state (equirect2cube)");
				}

				m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(envTextureUnfiltered.texture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS));
				m_commandList->SetDescriptorHeaps(1, computeDescriptorHeaps);
				m_commandList->SetPipelineState(pipelineState.Get());
				m_commandList->SetComputeRootSignature(computeRootSignature.Get());
				m_commandList->SetComputeRootDescriptorTable(0, envTextureEquirect.srv.gpuHandle);
				m_commandList->SetComputeRootDescriptorTable(1, envTextureUnfiltered.uav.gpuHandle);
				m_commandList->Dispatch(m_envTexture.width/32, m_envTexture.height/32, 6);
				m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(envTextureUnfiltered.texture.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON));

				// This will implicitly execute command list & wait for GPU to finish.
				generateMipmaps(envTextureUnfiltered);
			}

			// Compute pre-filtered specular environment map.
			{
				DescriptorHeapMark mark(m_descHeapCBV_SRV_UAV);

				ComPtr<ID3D12PipelineState> pipelineState;
				ComPtr<ID3DBlob> spmapShader = compileShader("shaders/hlsl/spmap.hlsl", "main", "cs_5_0");

				D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
				psoDesc.pRootSignature = computeRootSignature.Get();
				psoDesc.CS = CD3DX12_SHADER_BYTECODE{spmapShader.Get()};
				if(FAILED(m_device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&pipelineState)))) {
					throw std::runtime_error("Failed to create compute pipeline state (spmap)");
				}

				// Copy 0th mipmap level into destination environment map.
				const D3D12_RESOURCE_BARRIER preCopyBarriers[] = {
					CD3DX12_RESOURCE_BARRIER::Transition(m_envTexture.texture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST),
					CD3DX12_RESOURCE_BARRIER::Transition(envTextureUnfiltered.texture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE)
				};
				const D3D12_RESOURCE_BARRIER postCopyBarriers[] = {
					CD3DX12_RESOURCE_BARRIER::Transition(m_envTexture.texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS),
					CD3DX12_RESOURCE_BARRIER::Transition(envTextureUnfiltered.texture.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)
				};
				m_commandList->ResourceBarrier(2, preCopyBarriers);
				for(UINT arraySlice=0; arraySlice<6; ++arraySlice) {
					const UINT subresourceIndex = D3D12CalcSubresource(0, arraySlice, 0, m_envTexture.levels, 6);
					m_commandList->CopyTextureRegion(&CD3DX12_TEXTURE_COPY_LOCATION{m_envTexture.texture.Get(), subresourceIndex}, 0, 0, 0, &CD3DX12_TEXTURE_COPY_LOCATION{envTextureUnfiltered.texture.Get(), subresourceIndex}, nullptr);
				}
				m_commandList->ResourceBarrier(2, postCopyBarriers);

				// Pre-filter rest of the mip chain.
				m_commandList->SetDescriptorHeaps(1, computeDescriptorHeaps);
				m_commandList->SetPipelineState(pipelineState.Get());
				m_commandList->SetComputeRootSignature(computeRootSignature.Get());
				m_commandList->SetComputeRootDescriptorTable(0, envTextureUnfiltered.srv.gpuHandle);

				const float deltaRoughness = 1.0f / std::max(float(m_envTexture.levels-1), 1.0f);
				for(UINT level=1, size=512; level<m_envTexture.levels; ++level, size/=2) {
					const UINT numGroups = std::max<UINT>(1, size/32);
					const float spmapRoughness = level * deltaRoughness;

					createTextureUAV(m_envTexture, level);

					m_commandList->SetComputeRootDescriptorTable(1, m_envTexture.uav.gpuHandle);
					m_commandList->SetComputeRoot32BitConstants(2, 1, &spmapRoughness, 0);
					m_commandList->Dispatch(numGroups, numGroups, 6);
				}
				m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_envTexture.texture.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON));

				executeCommandList();
				waitForGPU();
			}
		}

		// Compute diffuse irradiance cubemap.
		m_irmapTexture = createTexture(32, 32, 6, DXGI_FORMAT_R16G16B16A16_FLOAT, 1);
		{
			DescriptorHeapMark mark(m_descHeapCBV_SRV_UAV);
			createTextureUAV(m_irmapTexture, 0);

			ComPtr<ID3D12PipelineState> pipelineState;
			ComPtr<ID3DBlob> irmapShader = compileShader("shaders/hlsl/irmap.hlsl", "main", "cs_5_0");

			D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
			psoDesc.pRootSignature = computeRootSignature.Get();
			psoDesc.CS = CD3DX12_SHADER_BYTECODE{irmapShader.Get()};
			if(FAILED(m_device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&pipelineState)))) {
				throw std::runtime_error("Failed to create compute pipeline state (irmap)");
			}

			m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_irmapTexture.texture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS));
			m_commandList->SetDescriptorHeaps(1, computeDescriptorHeaps);
			m_commandList->SetPipelineState(pipelineState.Get());
			m_commandList->SetComputeRootSignature(computeRootSignature.Get());
			m_commandList->SetComputeRootDescriptorTable(0, m_envTexture.srv.gpuHandle);
			m_commandList->SetComputeRootDescriptorTable(1, m_irmapTexture.uav.gpuHandle);
			m_commandList->Dispatch(m_irmapTexture.width/32, m_irmapTexture.height/32, 6);
			m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_irmapTexture.texture.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON));

			executeCommandList();
			waitForGPU();
		}

		// Compute Cook-Torrance BRDF 2D LUT for split-sum approximation.
		m_spBRDF_LUT = createTexture(256, 256, 1, DXGI_FORMAT_R16G16_FLOAT, 1);
		{
			DescriptorHeapMark mark(m_descHeapCBV_SRV_UAV);
			createTextureUAV(m_spBRDF_LUT, 0);

			ComPtr<ID3D12PipelineState> pipelineState;
			ComPtr<ID3DBlob> spBRDFShader = compileShader("shaders/hlsl/spbrdf.hlsl", "main", "cs_5_0");

			D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
			psoDesc.pRootSignature = computeRootSignature.Get();
			psoDesc.CS = CD3DX12_SHADER_BYTECODE{spBRDFShader.Get()};
			if(FAILED(m_device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&pipelineState)))) {
				throw std::runtime_error("Failed to create compute pipeline state (spbrdf)");
			}

			m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_spBRDF_LUT.texture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS));
			m_commandList->SetDescriptorHeaps(1, computeDescriptorHeaps);
			m_commandList->SetPipelineState(pipelineState.Get());
			m_commandList->SetComputeRootSignature(computeRootSignature.Get());
			m_commandList->SetComputeRootDescriptorTable(1, m_spBRDF_LUT.uav.gpuHandle);
			m_commandList->Dispatch(m_spBRDF_LUT.width/32, m_spBRDF_LUT.height/32, 1);
			m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_spBRDF_LUT.texture.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON));

			executeCommandList();
			waitForGPU();
		}
	}

	// Create 64kB host-mapped buffer in the upload heap for shader constants.
	m_constantBuffer = createUploadBuffer(64 * 1024);

	// Configure per-frame resources.
	{
		std::vector<D3D12_RESOURCE_BARRIER> barriers{NumFrames};
		for(UINT frameIndex=0; frameIndex<NumFrames; ++frameIndex) {
			m_transformCBVs[frameIndex] = createConstantBufferView<TransformCB>();
			m_shadingCBVs[frameIndex] = createConstantBufferView<ShadingCB>();

			barriers[frameIndex] = CD3DX12_RESOURCE_BARRIER::Transition(m_resolveFramebuffers[frameIndex].colorTexture.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
		}
		m_commandList->ResourceBarrier(NumFrames, barriers.data());
	}

	executeCommandList(false);
	waitForGPU();
}

void Renderer::render(GLFWwindow* window, const ViewSettings& view, const SceneSettings& scene)
{
	const glm::mat4 projectionMatrix = glm::perspectiveFov(view.fov, float(1024), float(1024), 1.0f, 1000.0f);
	const glm::mat4 viewRotationMatrix = glm::eulerAngleXY(glm::radians(view.pitch), glm::radians(view.yaw));
	const glm::mat4 sceneRotationMatrix = glm::eulerAngleXY(glm::radians(scene.pitch), glm::radians(scene.yaw));
	const glm::mat4 viewMatrix = glm::translate(glm::mat4{ 1.0f }, { 0.0f, 0.0f, -view.distance }) * viewRotationMatrix;
	const glm::vec3 eyePosition = glm::inverse(viewMatrix)[3];

	const ConstantBufferView& transformCBV = m_transformCBVs[m_frameIndex];
	const ConstantBufferView& shadingCBV = m_shadingCBVs[m_frameIndex];

	// Update transform constant buffer (for vertex shaders).
	{
		TransformCB* transformConstants = transformCBV.as<TransformCB>();
		transformConstants->viewProjectionMatrix = projectionMatrix * viewMatrix;
		transformConstants->skyProjectionMatrix  = projectionMatrix * viewRotationMatrix;
		transformConstants->sceneRotationMatrix  = sceneRotationMatrix;
	}

	// Update shading constant buffer (for pixel shader).
	{
		ShadingCB* shadingConstants = shadingCBV.as<ShadingCB>();
		shadingConstants->eyePosition = glm::vec4{eyePosition, 0.0f};
		for(int i=0; i<SceneSettings::NumLights; ++i) {
			const SceneSettings::Light& light = scene.lights[i];
			shadingConstants->lights[i].direction = glm::vec4{light.direction, 0.0f};
			if(light.enabled) {
				shadingConstants->lights[i].radiance = glm::vec4{light.radiance, 0.0f};
			}
			else {
				shadingConstants->lights[i].radiance = glm::vec4{};
			}
		}
	}

	ID3D12CommandAllocator* commandAllocator = m_commandAllocators[m_frameIndex].Get();
	const SwapChainBuffer& backbuffer = m_backbuffers[m_frameIndex];
	const FrameBuffer& framebuffer = m_framebuffers[m_frameIndex];
	const FrameBuffer& resolveFramebuffer = m_resolveFramebuffers[m_frameIndex];

	commandAllocator->Reset();
	m_commandList->Reset(commandAllocator, m_skyboxPipelineState.Get());

	// Set global state.
	m_commandList->RSSetViewports(1, &CD3DX12_VIEWPORT{0.0f, 0.0f, (FLOAT)framebuffer.width, (FLOAT)framebuffer.height});
	m_commandList->RSSetScissorRects(1, &CD3DX12_RECT{0, 0, (LONG)framebuffer.width, (LONG)framebuffer.height});

	ID3D12DescriptorHeap* descriptorHeaps[] = {
		m_descHeapCBV_SRV_UAV.heap.Get()
	};
	m_commandList->SetDescriptorHeaps(1, descriptorHeaps);

	// If not using MSAA, transition main framebuffer into render target state.
	if(framebuffer.samples <= 1) {
		m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(framebuffer.colorTexture.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET));
	}

	// Prepare for rendering into the main framebuffer.
	m_commandList->OMSetRenderTargets(1, &framebuffer.rtv.cpuHandle, false, &framebuffer.dsv.cpuHandle);
	m_commandList->ClearDepthStencilView(framebuffer.dsv.cpuHandle, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
	m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// Draw skybox.
	{
		m_commandList->SetGraphicsRootSignature(m_skyboxRootSignature.Get());
		m_commandList->SetGraphicsRootDescriptorTable(0, transformCBV.cbv.gpuHandle);
		m_commandList->SetGraphicsRootDescriptorTable(1, m_envTexture.srv.gpuHandle);

		m_commandList->IASetVertexBuffers(0, 1, &m_skybox.vbv);
		m_commandList->IASetIndexBuffer(&m_skybox.ibv);

		m_commandList->DrawIndexedInstanced(m_skybox.numElements, 1, 0, 0, 0);
	}

	// Draw PBR model.
	{
		m_commandList->SetGraphicsRootSignature(m_pbrRootSignature.Get());
		m_commandList->SetGraphicsRootDescriptorTable(0, transformCBV.cbv.gpuHandle);
		m_commandList->SetGraphicsRootDescriptorTable(1, shadingCBV.cbv.gpuHandle);
		m_commandList->SetGraphicsRootDescriptorTable(2, m_albedoTexture.srv.gpuHandle);
		m_commandList->SetPipelineState(m_pbrPipelineState.Get());

		m_commandList->IASetVertexBuffers(0, 1, &m_pbrModel.vbv);
		m_commandList->IASetIndexBuffer(&m_pbrModel.ibv);

		m_commandList->DrawIndexedInstanced(m_pbrModel.numElements, 1, 0, 0, 0);
	}

	// Resolve multisample framebuffer (MSAA) or transition into pixel shader resource state (non-MSAA).
	if(framebuffer.samples > 1) {
		resolveFrameBuffer(framebuffer, resolveFramebuffer, DXGI_FORMAT_R16G16B16A16_FLOAT);
	}
	else {
		m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(framebuffer.colorTexture.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE));
	}

	// Prepare for rendering directly into a back buffer.
	m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(backbuffer.buffer.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
	m_commandList->OMSetRenderTargets(1, &backbuffer.rtv.cpuHandle, false, nullptr);

	// Draw a full screen triangle for postprocessing/tone mapping.
	{
		m_commandList->SetGraphicsRootSignature(m_tonemapRootSignature.Get());
		m_commandList->SetGraphicsRootDescriptorTable(0, resolveFramebuffer.srv.gpuHandle);
		m_commandList->SetPipelineState(m_tonemapPipelineState.Get());

		m_commandList->DrawInstanced(3, 1, 0, 0);
	}

	m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(backbuffer.buffer.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

	executeCommandList(false);
	presentFrame();
}

DescriptorHeap Renderer::createDescriptorHeap(const D3D12_DESCRIPTOR_HEAP_DESC& desc) const
{
	DescriptorHeap heap;
	if(FAILED(m_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&heap.heap)))) {
		throw std::runtime_error("Failed to create descriptor heap");
	}
	heap.numDescriptorsAllocated = 0;
	heap.numDescriptorsInHeap = desc.NumDescriptors;
	heap.descriptorSize = m_device->GetDescriptorHandleIncrementSize(desc.Type);
	return heap;
}

MeshBuffer Renderer::createMeshBuffer(const std::shared_ptr<Mesh>& mesh) const
{
	MeshBuffer buffer;
	buffer.numElements = static_cast<UINT>(mesh->faces().size() * 3);

	const size_t vertexDataSize = mesh->vertices().size() * sizeof(Mesh::Vertex);
	const size_t indexDataSize = mesh->faces().size() * sizeof(Mesh::Face);

	// Create GPU resources & initialize view structures.

	if(FAILED(m_device->CreateCommittedResource(
		&CD3DX12_HEAP_PROPERTIES{D3D12_HEAP_TYPE_DEFAULT},
		D3D12_HEAP_FLAG_NONE,
		&CD3DX12_RESOURCE_DESC::Buffer(vertexDataSize),
		D3D12_RESOURCE_STATE_COPY_DEST,
		nullptr,
		IID_PPV_ARGS(&buffer.vertexBuffer))))
	{
		throw std::runtime_error("Failed to create vertex buffer");
	}
	buffer.vbv.BufferLocation = buffer.vertexBuffer->GetGPUVirtualAddress();
	buffer.vbv.SizeInBytes = static_cast<UINT>(vertexDataSize);
	buffer.vbv.StrideInBytes = sizeof(Mesh::Vertex);

	if(FAILED(m_device->CreateCommittedResource(
		&CD3DX12_HEAP_PROPERTIES{D3D12_HEAP_TYPE_DEFAULT},
		D3D12_HEAP_FLAG_NONE,
		&CD3DX12_RESOURCE_DESC::Buffer(indexDataSize),
		D3D12_RESOURCE_STATE_COPY_DEST,
		nullptr,
		IID_PPV_ARGS(&buffer.indexBuffer))))
	{
		throw std::runtime_error("Failed to create index buffer");
	}
	buffer.ibv.BufferLocation = buffer.indexBuffer->GetGPUVirtualAddress();
	buffer.ibv.SizeInBytes = static_cast<UINT>(indexDataSize);
	buffer.ibv.Format = DXGI_FORMAT_R32_UINT;

	// Create and upload to CPU accessible staging buffers.
	StagingBuffer vertexStagingBuffer;
	{
		const D3D12_SUBRESOURCE_DATA data = { mesh->vertices().data() };
		vertexStagingBuffer = createStagingBuffer(buffer.vertexBuffer, 0, 1, &data);
	}
	StagingBuffer indexStagingBuffer;
	{
		const D3D12_SUBRESOURCE_DATA data = { mesh->faces().data() };
		indexStagingBuffer = createStagingBuffer(buffer.indexBuffer, 0, 1, &data);
	}

	// Enqueue upload to the GPU.
	m_commandList->CopyResource(buffer.vertexBuffer.Get(), vertexStagingBuffer.buffer.Get());
	m_commandList->CopyResource(buffer.indexBuffer.Get(), indexStagingBuffer.buffer.Get());

	const D3D12_RESOURCE_BARRIER barriers[] = {
		CD3DX12_RESOURCE_BARRIER::Transition(buffer.vertexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER),
		CD3DX12_RESOURCE_BARRIER::Transition(buffer.indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER)
	};
	m_commandList->ResourceBarrier(2, barriers);

	// Execute graphics command list and wait for GPU to finish.
	executeCommandList();
	waitForGPU();

	return buffer;
}

UploadBuffer Renderer::createUploadBuffer(UINT capacity) const
{
	UploadBuffer buffer;
	buffer.cursor   = 0;
	buffer.capacity = capacity;

	if(FAILED(m_device->CreateCommittedResource(
		&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
		D3D12_HEAP_FLAG_NONE,
		&CD3DX12_RESOURCE_DESC::Buffer(capacity),
		D3D12_RESOURCE_STATE_GENERIC_READ,
		nullptr,
		IID_PPV_ARGS(&buffer.buffer))))
	{
		throw std::runtime_error("Failed to create GPU upload buffer");
	}
	if(FAILED(buffer.buffer->Map(0, &CD3DX12_RANGE{0, 0}, reinterpret_cast<void**>(&buffer.cpuAddress)))) {
		throw std::runtime_error("Failed to map GPU upload buffer to host address space");
	}
	buffer.gpuAddress = buffer.buffer->GetGPUVirtualAddress();
	return buffer;
}

UploadBufferRegion Renderer::allocFromUploadBuffer(UploadBuffer& buffer, UINT size, int align) const
{
	const UINT alignedSize = Utility::roundToPowerOfTwo(size, align);
	if(buffer.cursor + alignedSize > buffer.capacity) {
		throw std::overflow_error("Out of upload buffer capacity while allocating memory");
	}

	UploadBufferRegion region;
	region.cpuAddress = reinterpret_cast<void*>(buffer.cpuAddress + buffer.cursor);
	region.gpuAddress = buffer.gpuAddress + buffer.cursor;
	region.size = alignedSize;
	buffer.cursor += alignedSize;
	return region;
}

StagingBuffer Renderer::createStagingBuffer(const ComPtr<ID3D12Resource>& resource, UINT firstSubresource, UINT numSubresources, const D3D12_SUBRESOURCE_DATA* data) const
{
	StagingBuffer stagingBuffer;
	stagingBuffer.firstSubresource = firstSubresource;
	stagingBuffer.numSubresources = numSubresources;
	stagingBuffer.layouts.resize(numSubresources);

	const D3D12_RESOURCE_DESC resourceDesc = resource->GetDesc();

	UINT64 numBytesTotal;
	std::vector<UINT> numRows{numSubresources};
	std::vector<UINT64> rowBytes{numSubresources};
	m_device->GetCopyableFootprints(&resourceDesc, firstSubresource, numSubresources, 0, stagingBuffer.layouts.data(), numRows.data(), rowBytes.data(), &numBytesTotal);

	if(FAILED(m_device->CreateCommittedResource(
		&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
		D3D12_HEAP_FLAG_NONE,
		&CD3DX12_RESOURCE_DESC::Buffer(numBytesTotal),
		D3D12_RESOURCE_STATE_GENERIC_READ,
		nullptr,
		IID_PPV_ARGS(&stagingBuffer.buffer))))
	{
		throw std::runtime_error("Failed to create GPU staging buffer");
	}

	if(data) {
		assert(resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D);

		void* bufferMemory;
		if(FAILED(stagingBuffer.buffer->Map(0, &CD3DX12_RANGE{0, 0}, &bufferMemory))) {
			throw std::runtime_error("Failed to map GPU staging buffer to host address space");
		}

		for(UINT subresource=0; subresource<numSubresources; ++subresource) {
			uint8_t* subresourceMemory = reinterpret_cast<uint8_t*>(bufferMemory) + stagingBuffer.layouts[subresource].Offset;
			if(resourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) {
				// Generic buffer: Copy everything in one go.
				std::memcpy(subresourceMemory, data->pData, numBytesTotal);
			}
			else {
				// Texture: Copy data line by line.
				for(UINT row=0; row<numRows[subresource]; ++row) {
					const uint8_t* srcRowPtr = reinterpret_cast<const uint8_t*>(data[subresource].pData) + row * data[subresource].RowPitch;
					uint8_t* destRowPtr = subresourceMemory + row * stagingBuffer.layouts[subresource].Footprint.RowPitch;
					std::memcpy(destRowPtr, srcRowPtr, rowBytes[subresource]);
				}
			}
		}
		stagingBuffer.buffer->Unmap(0, nullptr);
	}
	return stagingBuffer;
}

Texture Renderer::createTexture(UINT width, UINT height, UINT depth, DXGI_FORMAT format, UINT levels)
{
	assert(depth == 1 || depth == 6);

	Texture texture;
	texture.width = width;
	texture.height = height;
	texture.levels = (levels > 0) ? levels : Utility::numMipmapLevels(width, height);

	D3D12_RESOURCE_DESC desc = {};
	desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
	desc.Width  = width;
	desc.Height = height;
	desc.DepthOrArraySize = depth;
	desc.MipLevels = levels;
	desc.Format = format;
	desc.SampleDesc.Count = 1;
	desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;

	if(FAILED(m_device->CreateCommittedResource(
		&CD3DX12_HEAP_PROPERTIES{D3D12_HEAP_TYPE_DEFAULT},
		D3D12_HEAP_FLAG_NONE,
		&desc,
		D3D12_RESOURCE_STATE_COMMON,
		nullptr,
		IID_PPV_ARGS(&texture.texture))))
	{
		throw std::runtime_error("Failed to create 2D texture");
	}

	D3D12_SRV_DIMENSION srvDim;
	switch(depth) {
	case 1:  srvDim = D3D12_SRV_DIMENSION_TEXTURE2D; break;
	case 6:  srvDim = D3D12_SRV_DIMENSION_TEXTURECUBE; break;
	default: srvDim = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; break;
	}
	createTextureSRV(texture, srvDim);
	return texture;
}

Texture Renderer::createTexture(const std::shared_ptr<Image>& image, DXGI_FORMAT format, UINT levels)
{
	Texture texture = createTexture(image->width(), image->height(), 1, format, levels);
	StagingBuffer textureStagingBuffer;
	{
		const D3D12_SUBRESOURCE_DATA data{ image->pixels<void>(), image->pitch() };
		textureStagingBuffer = createStagingBuffer(texture.texture, 0, 1, &data);
	}

	{
		const CD3DX12_TEXTURE_COPY_LOCATION destCopyLocation{texture.texture.Get(), 0};
		const CD3DX12_TEXTURE_COPY_LOCATION srcCopyLocation{textureStagingBuffer.buffer.Get(), textureStagingBuffer.layouts[0]};

		m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture.texture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST, 0));
		m_commandList->CopyTextureRegion(&destCopyLocation, 0, 0, 0, &srcCopyLocation, nullptr);
		m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture.texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON, 0));
	}

	if(texture.levels > 1 && texture.width == texture.height && Utility::isPowerOfTwo(texture.width)) {
		generateMipmaps(texture);
	}
	else {
		// Only execute & wait if not generating mipmaps (so that we don't wait redundantly).
		executeCommandList();
		waitForGPU();
	}
	return texture;
}

void Renderer::generateMipmaps(const Texture& texture)
{
	assert(texture.width == texture.height);
	assert(Utility::isPowerOfTwo(texture.width));

	if(!m_mipmapGeneration.rootSignature) {
		const CD3DX12_DESCRIPTOR_RANGE1 descriptorRanges[] = {
			{D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE},
			{D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE},
		};
		CD3DX12_ROOT_PARAMETER1 rootParameters[2];
		rootParameters[0].InitAsDescriptorTable(1, &descriptorRanges[0]);
		rootParameters[1].InitAsDescriptorTable(1, &descriptorRanges[1]);

		CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
		rootSignatureDesc.Init_1_1(2, rootParameters);
		m_mipmapGeneration.rootSignature = createRootSignature(rootSignatureDesc);
	}

	ID3D12PipelineState* pipelineState = nullptr;

	const D3D12_RESOURCE_DESC desc = texture.texture->GetDesc();
	if(desc.DepthOrArraySize == 1 && desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) {
		if(!m_mipmapGeneration.gammaTexturePipelineState) {
			ComPtr<ID3DBlob> computeShader = compileShader("shaders/hlsl/downsample.hlsl", "downsample_gamma", "cs_5_0");
			D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
			psoDesc.pRootSignature = m_mipmapGeneration.rootSignature.Get();
			psoDesc.CS = CD3DX12_SHADER_BYTECODE(computeShader.Get());
			if(FAILED(m_device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&m_mipmapGeneration.gammaTexturePipelineState)))) {
				throw std::runtime_error("Failed to create compute pipeline state (gamma correct downsample filter)");
			}
		}
		pipelineState = m_mipmapGeneration.gammaTexturePipelineState.Get();
	}
	else if(desc.DepthOrArraySize > 1 && desc.Format != DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) {
		if(!m_mipmapGeneration.arrayTexturePipelineState) {
			ComPtr<ID3DBlob> computeShader = compileShader("shaders/hlsl/downsample_array.hlsl", "downsample_linear", "cs_5_0");
			D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
			psoDesc.pRootSignature = m_mipmapGeneration.rootSignature.Get();
			psoDesc.CS = CD3DX12_SHADER_BYTECODE(computeShader.Get());
			if(FAILED(m_device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&m_mipmapGeneration.arrayTexturePipelineState)))) {
				throw std::runtime_error("Failed to create compute pipeline state (array downsample filter)");
			}
		}
		pipelineState = m_mipmapGeneration.arrayTexturePipelineState.Get();
	}
	else {
		assert(desc.DepthOrArraySize == 1);
		if(!m_mipmapGeneration.linearTexturePipelineState) {
			ComPtr<ID3DBlob> computeShader = compileShader("shaders/hlsl/downsample.hlsl", "downsample_linear", "cs_5_0");
			D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {};
			psoDesc.pRootSignature = m_mipmapGeneration.rootSignature.Get();
			psoDesc.CS = CD3DX12_SHADER_BYTECODE(computeShader.Get());
			if(FAILED(m_device->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&m_mipmapGeneration.linearTexturePipelineState)))) {
				throw std::runtime_error("Failed to create compute pipeline state (linear downsample filter)");
			}
		}
		pipelineState = m_mipmapGeneration.gammaTexturePipelineState.Get();
	}

	DescriptorHeapMark mark(m_descHeapCBV_SRV_UAV);

	Texture linearTexture = texture;
	if(desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) {
		pipelineState = m_mipmapGeneration.gammaTexturePipelineState.Get();
		linearTexture = createTexture(texture.width, texture.height, 1, DXGI_FORMAT_R8G8B8A8_UNORM, texture.levels);

		m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(linearTexture.texture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST));
		m_commandList->CopyResource(linearTexture.texture.Get(), texture.texture.Get());
		m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(linearTexture.texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON));
	}

	ID3D12DescriptorHeap* descriptorHeaps[] = {
		m_descHeapCBV_SRV_UAV.heap.Get()
	};

	m_commandList->SetComputeRootSignature(m_mipmapGeneration.rootSignature.Get());
	m_commandList->SetDescriptorHeaps(1, descriptorHeaps);
	m_commandList->SetPipelineState(pipelineState);

	std::vector<CD3DX12_RESOURCE_BARRIER> preDispatchBarriers{desc.DepthOrArraySize};
	std::vector<CD3DX12_RESOURCE_BARRIER> postDispatchBarriers{desc.DepthOrArraySize};
	for(UINT level=1, levelWidth=texture.width/2, levelHeight=texture.height/2; level<texture.levels; ++level, levelWidth/=2, levelHeight/=2) {
		createTextureSRV(linearTexture, desc.DepthOrArraySize > 1 ? D3D12_SRV_DIMENSION_TEXTURE2DARRAY : D3D12_SRV_DIMENSION_TEXTURE2D, level-1, 1);
		createTextureUAV(linearTexture, level);

		for(UINT arraySlice=0; arraySlice<desc.DepthOrArraySize; ++arraySlice) {
			const UINT subresourceIndex = D3D12CalcSubresource(level, arraySlice, 0, texture.levels, desc.DepthOrArraySize);
			preDispatchBarriers[arraySlice] = CD3DX12_RESOURCE_BARRIER::Transition(linearTexture.texture.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, subresourceIndex);
			postDispatchBarriers[arraySlice] = CD3DX12_RESOURCE_BARRIER::Transition(linearTexture.texture.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COMMON, subresourceIndex);
		}

		m_commandList->ResourceBarrier(desc.DepthOrArraySize, preDispatchBarriers.data());
		m_commandList->SetComputeRootDescriptorTable(0, linearTexture.srv.gpuHandle);
		m_commandList->SetComputeRootDescriptorTable(1, linearTexture.uav.gpuHandle);
		m_commandList->Dispatch(glm::max(UINT(1), levelWidth/8), glm::max(UINT(1), levelHeight/8), desc.DepthOrArraySize);
		m_commandList->ResourceBarrier(desc.DepthOrArraySize, postDispatchBarriers.data());
	}

	if(texture.texture == linearTexture.texture) {
		m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture.texture.Get(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_COMMON));
	}
	else {
		m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture.texture.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COPY_DEST));
		m_commandList->CopyResource(texture.texture.Get(), linearTexture.texture.Get());
		m_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(texture.texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON));
	}

	executeCommandList();
	waitForGPU();
}

void Renderer::createTextureSRV(Texture& texture, D3D12_SRV_DIMENSION dimension, UINT mostDetailedMip, UINT mipLevels)
{
	const D3D12_RESOURCE_DESC desc = texture.texture->GetDesc();
	const UINT effectiveMipLevels = (mipLevels > 0) ? mipLevels : (desc.MipLevels - mostDetailedMip);
	assert(!(desc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE));

	texture.srv = m_descHeapCBV_SRV_UAV.alloc();

	D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
	srvDesc.Format = desc.Format;
	srvDesc.ViewDimension = dimension;
	srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
	switch(dimension) {
	case D3D12_SRV_DIMENSION_TEXTURE2D:
		srvDesc.Texture2D.MostDetailedMip = mostDetailedMip;
		srvDesc.Texture2D.MipLevels = effectiveMipLevels;
		break;
	case D3D12_SRV_DIMENSION_TEXTURE2DARRAY:
		srvDesc.Texture2DArray.MostDetailedMip = mostDetailedMip;
		srvDesc.Texture2DArray.MipLevels = effectiveMipLevels;
		srvDesc.Texture2DArray.FirstArraySlice = 0;
		srvDesc.Texture2DArray.ArraySize = desc.DepthOrArraySize;
		break;
	case D3D12_SRV_DIMENSION_TEXTURECUBE:
		assert(desc.DepthOrArraySize == 6);
		srvDesc.TextureCube.MostDetailedMip = mostDetailedMip;
		srvDesc.TextureCube.MipLevels = effectiveMipLevels;
		break;
	default:
		assert(0);
	}
	m_device->CreateShaderResourceView(texture.texture.Get(), &srvDesc, texture.srv.cpuHandle);
}

void Renderer::createTextureUAV(Texture& texture, UINT mipSlice)
{
	const D3D12_RESOURCE_DESC desc = texture.texture->GetDesc();
	assert(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);

	texture.uav = m_descHeapCBV_SRV_UAV.alloc();

	D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
	uavDesc.Format = desc.Format;
	if(desc.DepthOrArraySize > 1) {
		uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
		uavDesc.Texture2DArray.MipSlice = mipSlice;
		uavDesc.Texture2DArray.FirstArraySlice = 0;
		uavDesc.Texture2DArray.ArraySize = desc.DepthOrArraySize;
	}
	else {
		uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
		uavDesc.Texture2D.MipSlice = mipSlice;
	}
	m_device->CreateUnorderedAccessView(texture.texture.Get(), nullptr, &uavDesc, texture.uav.cpuHandle);
}

FrameBuffer Renderer::createFrameBuffer(UINT width, UINT height, UINT samples, DXGI_FORMAT colorFormat, DXGI_FORMAT depthstencilFormat)
{
	FrameBuffer fb = {};
	fb.width   = width;
	fb.height  = height;
	fb.samples = samples;

	D3D12_RESOURCE_DESC desc = {};
	desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
	desc.Width = width;
	desc.Height = height;
	desc.DepthOrArraySize = 1;
	desc.MipLevels = 1;
	desc.SampleDesc.Count = samples;

	if(colorFormat != DXGI_FORMAT_UNKNOWN) {
		desc.Format = colorFormat;
		desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;

		const float optimizedClearColor[] = {0.0f, 0.0f, 0.0f, 0.0f};
		if(FAILED(m_device->CreateCommittedResource(
			&CD3DX12_HEAP_PROPERTIES{D3D12_HEAP_TYPE_DEFAULT},
			D3D12_HEAP_FLAG_NONE,
			&desc,
			D3D12_RESOURCE_STATE_RENDER_TARGET,
			&CD3DX12_CLEAR_VALUE{colorFormat, optimizedClearColor},
			IID_PPV_ARGS(&fb.colorTexture))))
		{
			throw std::runtime_error("Failed to create FrameBuffer color texture");
		}

		D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
		rtvDesc.Format = desc.Format;
		rtvDesc.ViewDimension = (samples > 1) ? D3D12_RTV_DIMENSION_TEXTURE2DMS : D3D12_RTV_DIMENSION_TEXTURE2D;

		fb.rtv = m_descHeapRTV.alloc();
		m_device->CreateRenderTargetView(fb.colorTexture.Get(), &rtvDesc, fb.rtv.cpuHandle);

		if(samples <= 1) {
			D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
			srvDesc.Format = desc.Format;
			srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
			srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
			srvDesc.Texture2D.MostDetailedMip = 0;
			srvDesc.Texture2D.MipLevels = 1;

			fb.srv = m_descHeapCBV_SRV_UAV.alloc();
			m_device->CreateShaderResourceView(fb.colorTexture.Get(), &srvDesc, fb.srv.cpuHandle);
		}
	}

	if(depthstencilFormat != DXGI_FORMAT_UNKNOWN) {
		desc.Format = depthstencilFormat;
		desc.Flags  = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;

		if(FAILED(m_device->CreateCommittedResource(
			&CD3DX12_HEAP_PROPERTIES{D3D12_HEAP_TYPE_DEFAULT},
			D3D12_HEAP_FLAG_NONE,
			&desc,
			D3D12_RESOURCE_STATE_DEPTH_WRITE,
			&CD3DX12_CLEAR_VALUE{depthstencilFormat, 1.0f, 0},
			IID_PPV_ARGS(&fb.depthStencilTexture))))
		{
			throw std::runtime_error("Failed to create FrameBuffer depth-stencil texture");
		}

		D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
		dsvDesc.Format = desc.Format;
		dsvDesc.ViewDimension = (samples > 1) ? D3D12_DSV_DIMENSION_TEXTURE2DMS : D3D12_DSV_DIMENSION_TEXTURE2D;

		fb.dsv = m_descHeapDSV.alloc();
		m_device->CreateDepthStencilView(fb.depthStencilTexture.Get(), &dsvDesc, fb.dsv.cpuHandle);
	}
	return fb;
}

void Renderer::resolveFrameBuffer(const FrameBuffer& srcfb, const FrameBuffer& dstfb, DXGI_FORMAT format) const
{
	const CD3DX12_RESOURCE_BARRIER preResolveBarriers[] = {
		CD3DX12_RESOURCE_BARRIER::Transition(srcfb.colorTexture.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE),
		CD3DX12_RESOURCE_BARRIER::Transition(dstfb.colorTexture.Get(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RESOLVE_DEST)
	};
	const CD3DX12_RESOURCE_BARRIER postResolveBarriers[] = {
		CD3DX12_RESOURCE_BARRIER::Transition(srcfb.colorTexture.Get(), D3D12_RESOURCE_STATE_RESOLVE_SOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET),
		CD3DX12_RESOURCE_BARRIER::Transition(dstfb.colorTexture.Get(), D3D12_RESOURCE_STATE_RESOLVE_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
	};

	if(srcfb.colorTexture != dstfb.colorTexture) {
		m_commandList->ResourceBarrier(2, preResolveBarriers);
		m_commandList->ResolveSubresource(dstfb.colorTexture.Get(), 0, srcfb.colorTexture.Get(), 0, format);
		m_commandList->ResourceBarrier(2, postResolveBarriers);
	}
}

ComPtr<ID3D12RootSignature> Renderer::createRootSignature(D3D12_VERSIONED_ROOT_SIGNATURE_DESC& desc) const
{
	const D3D12_ROOT_SIGNATURE_FLAGS standardFlags =
		D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
		D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS |
		D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS;

	switch(desc.Version) {
	case D3D_ROOT_SIGNATURE_VERSION_1_0:
		desc.Desc_1_0.Flags |= standardFlags;
		break;
	case D3D_ROOT_SIGNATURE_VERSION_1_1:
		desc.Desc_1_1.Flags |= standardFlags;
		break;
	}

	ComPtr<ID3D12RootSignature> rootSignature;
	ComPtr<ID3DBlob> signatureBlob, errorBlob;
	if(FAILED(D3DX12SerializeVersionedRootSignature(&desc, m_rootSignatureVersion, &signatureBlob, &errorBlob))) {
		throw std::runtime_error("Failed to serialize root signature");
	}
	if(FAILED(m_device->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), IID_PPV_ARGS(&rootSignature)))) {
		throw std::runtime_error("Failed to create root signature");
	}
	return rootSignature;
}

ConstantBufferView Renderer::createConstantBufferView(const void* data, UINT size)
{
	ConstantBufferView view;
	view.data = allocFromUploadBuffer(m_constantBuffer, size, 256);
	view.cbv = m_descHeapCBV_SRV_UAV.alloc();
	if(data) {
		std::memcpy(view.data.cpuAddress, data, size);
	}

	D3D12_CONSTANT_BUFFER_VIEW_DESC desc = {};
	desc.BufferLocation = view.data.gpuAddress;
	desc.SizeInBytes = view.data.size;
	m_device->CreateConstantBufferView(&desc, view.cbv.cpuHandle);

	return view;
}

void Renderer::executeCommandList(bool reset) const
{
	if(FAILED(m_commandList->Close())) {
		throw std::runtime_error("Failed close command list (validation error or not in recording state)");
	}

	ID3D12CommandList* lists[] = { m_commandList.Get() };
	m_commandQueue->ExecuteCommandLists(1, lists);

	if(reset) {
		m_commandList->Reset(m_commandAllocators[m_frameIndex].Get(), nullptr);
	}
}

void Renderer::waitForGPU() const
{
	UINT64& fenceValue = m_fenceValues[m_frameIndex];
	++fenceValue;

	m_commandQueue->Signal(m_fence.Get(), fenceValue);
	m_fence->SetEventOnCompletion(fenceValue, m_fenceCompletionEvent);
	WaitForSingleObject(m_fenceCompletionEvent, INFINITE);
}

void Renderer::presentFrame()
{
	m_swapChain->Present(1, 0);

	const UINT64 prevFrameFenceValue = m_fenceValues[m_frameIndex];
	m_frameIndex = m_swapChain->GetCurrentBackBufferIndex();
	UINT64& currentFrameFenceValue = m_fenceValues[m_frameIndex];

	m_commandQueue->Signal(m_fence.Get(), prevFrameFenceValue);

	if(m_fence->GetCompletedValue() < currentFrameFenceValue) {
		m_fence->SetEventOnCompletion(currentFrameFenceValue, m_fenceCompletionEvent);
		WaitForSingleObject(m_fenceCompletionEvent, INFINITE);
	}
	currentFrameFenceValue = prevFrameFenceValue + 1;
}

ComPtr<IDXGIAdapter1> Renderer::getAdapter(const ComPtr<IDXGIFactory4>& factory)
{
	ComPtr<IDXGIAdapter1> adapter;
	for(UINT index=0; factory->EnumAdapters1(index, &adapter) != DXGI_ERROR_NOT_FOUND; ++index) {
		DXGI_ADAPTER_DESC1 desc;
		adapter->GetDesc1(&desc);
		if(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
			continue;
		}
		if(SUCCEEDED(D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) {
			return adapter;
		}
	}
	return nullptr;
}

ComPtr<ID3DBlob> Renderer::compileShader(const std::string& filename, const std::string& entryPoint, const std::string& profile)
{
	UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
#if _DEBUG
	flags |= D3DCOMPILE_DEBUG;
	flags |= D3DCOMPILE_SKIP_OPTIMIZATION;
#endif

	ComPtr<ID3DBlob> shader;
	ComPtr<ID3DBlob> errorBlob;

	std::printf("Compiling HLSL shader: %s [%s]\n", filename.c_str(), entryPoint.c_str());

	if(FAILED(D3DCompileFromFile(Utility::convertToUTF16(filename).c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entryPoint.c_str(), profile.c_str(), flags, 0, &shader, &errorBlob))) {
		std::string errorMsg = "Shader compilation failed: " + filename;
		if(errorBlob) {
			errorMsg += std::string("\n") + static_cast<const char*>(errorBlob->GetBufferPointer());
		}
		throw std::runtime_error(errorMsg);
	}
	return shader;
}

} // D3D12

#endif // ENABLE_D3D12