This is a bit of a cheat as it is mostly reusing some existing code, but it's a slightly modified version of a C++ AMP realtime GPU path tracer I wrote.

Most of the interesting code is here but there's some utility code in a couple of other files for setting up D3D, setting up the window etc.
#include "app.h"
#include "d3d11device.h"
#include <vector>
#include <fstream>
#include <chrono>
#include <iostream>
#include <initializer_list>
#include <random>
#include <cassert>
#include <cmath>
#include <amp.h>
#include <amp_graphics.h>
#include <Xinput.h>
#include <amputils.h>
using namespace std;
using namespace renderer;
using namespace concurrency;
using namespace concurrency::direct3d;
using namespace concurrency::graphics;
using namespace concurrency::graphics::direct3d;
using namespace concurrency::fast_math;
using namespace DirectX;
using namespace amputils;
namespace {
template<typename T, size_t N>
size_t ArraySize(T (&)[N]) {
return N;
}
vector<char> LoadFile(const char* filename) {
ifstream file(filename, ios_base::binary);
return vector<char>(istreambuf_iterator<char>(file), (istreambuf_iterator<char>()));
}
struct SimpleVertex
{
float x, y, z;
};
class Sphere
{
public:
Sphere(const Vector3& center_, float radius_) restrict(amp, cpu) : center(center_), radius(radius_) {}
Vector3 GetCenter() const restrict(amp, cpu) { return center; }
float GetRadius() const restrict(amp, cpu) { return radius; }
// Used for sampling a light from a point
Vector3 Sample(const Vector3& p, float u1, float u2, Vector3& normal) const restrict(amp, cpu);
float Pdf(const Vector3& p, const Vector3& /*wi*/) const restrict(amp, cpu) {
Vector3 pToCenter = center - p;
float sinThetaMax2 = radius * radius / Dot(pToCenter, pToCenter);
float cosThetaMax = sqrt(max(0.1f, 1.f - sinThetaMax2));
return UniformConePdf(cosThetaMax);
}
private:
Vector3 center;
float radius;
};
class DifferentialGeometry
{
public:
DifferentialGeometry() restrict(amp, cpu) : p(0.0f), n(0.0f) {}
DifferentialGeometry(const Vector3& p_, const Vector3& n_) restrict(amp, cpu) : p(p_), n(n_) {}
const Vector3& GetPoint() const restrict(amp, cpu) { return p; }
const Vector3& GetNormal() const restrict(amp, cpu) { return n; }
private:
Vector3 p;
Vector3 n;
};
bool IntersectP(const Ray& ray, const Sphere& sphere, float& t) restrict(amp, cpu)
{
const auto v = ray.GetOrigin() - sphere.GetCenter();
const auto a = Dot(ray.GetDirection(), ray.GetDirection());
const auto minusB = -2.f * Dot(ray.GetDirection(), v);
const auto c = Dot(v, v) - Square(sphere.GetRadius());
const auto discrim = (Square(minusB) - 4.f * a * c);
if (discrim < 0.f) {
return false;
}
const auto sqrtDiscrim = sqrt(discrim);
const auto tMax = minusB + sqrtDiscrim;
if (tMax < 0.f) {
return false;
}
const auto denominator = 1.f / (2.f * a);
const auto tMin = minusB - sqrtDiscrim;
t = (tMin < 0.f ? tMax : tMin) * denominator;
return true;
}
bool Intersect(const Ray& ray, const Sphere& sphere, float& t, DifferentialGeometry& dg) restrict(amp, cpu)
{
const auto v = ray.GetOrigin() - sphere.GetCenter();
const auto a = Dot(ray.GetDirection(), ray.GetDirection());
const auto minusB = -2.f * Dot(ray.GetDirection(), v);
const auto c = Dot(v, v) - Square(sphere.GetRadius());
const auto discrim = (Square(minusB) - 4.f * a * c);
if (discrim < 0.f) {
return false;
}
const auto sqrtDiscrim = sqrt(discrim);
const auto tMax = minusB + sqrtDiscrim;
if (tMax < 0.f) {
return false;
}
const auto denominator = 1.f / (2.f * a);
const auto tMin = minusB - sqrtDiscrim;
t = (tMin < 0.f ? tMax : tMin) * denominator;
Vector3 p = ray(t);
dg = DifferentialGeometry(p, Normalize(p - sphere.GetCenter()));
return true;
}
inline Vector3 Sphere::Sample(const Vector3& p, float u1, float u2, Vector3& normal) const restrict(amp, cpu) {
Vector3 pToCenter = center - p;
Vector3 wc = Normalize(pToCenter);
Vector3 wcX(0.0f), wcY(0.0f);
CoordinateSystem(wc, wcX, wcY);
float sinThetaMax2 = radius * radius / Dot(pToCenter, pToCenter);
float cosThetaMax = sqrt(max(0.1f, 1.f - sinThetaMax2));
float tHit;
DifferentialGeometry dg;
Ray r(p, UniformSampleCone(u1, u2, cosThetaMax, wcX, wcY, wc));
if (!Intersect(r, *this, tHit, dg))
tHit = Dot(pToCenter, Normalize(r.GetDirection()));
normal = dg.GetNormal();
return dg.GetPoint();
}
class Primitive
{
public:
Primitive(const Sphere& s_, const Vector3& color_) restrict(amp, cpu) : s(s_), color(color_) {}
const Sphere& GetSphere() const restrict(amp, cpu) { return s; }
const Vector3& GetColor() const restrict(amp, cpu) { return color; }
private:
Sphere s;
Vector3 color;
};
struct CameraSample
{
CameraSample(float x_, float y_) restrict(amp, cpu) : x(x_), y(y_) {}
float x, y;
};
class StratifiedSampler
{
public:
explicit StratifiedSampler(int n) restrict(amp, cpu) : mN(n) {}
int GetNumSamples() const restrict(amp, cpu) { return mN * mN; }
CameraSample Sample2D(amputils::Rng& rng, int i) restrict(amp, cpu) {
float dxy = 1.0f / mN;
float xOff = static_cast<float>(i % mN);
float yOff = static_cast<float>(i / mN);
return CameraSample((xOff + rng.GetUniform()) * dxy, (yOff + rng.GetUniform()) * dxy);
}
private:
StratifiedSampler& operator=(const StratifiedSampler&);
int mN;
};
class Camera
{
public:
Camera(int filmWidth, int filmHeight)
: mPos(0.0f, 0.0f, -20.0f),
mYaw(0.0f),
mPitch(0.0f),
mWorldToCamera(XMMatrixLookAtLH(XMVectorReplicate(0.0f), XMVectorReplicate(1.0f), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f))),
mCameraToWorld(XMMatrixInverse(nullptr, mWorldToCamera.GetXMMATRIX())),
mProjection(XMMatrixPerspectiveFovLH(3.1415f * 0.333f, 1.0f, 1e-2f, 1e3f)),
mInverseProjection(XMMatrixInverse(nullptr, mProjection.GetXMMATRIX())),
mFilmWidth(filmWidth),
mFilmHeight(filmHeight)
{
UpdateMatrices();
}
const Matrix4& GetWorldToCamera() const restrict(amp, cpu) { return mWorldToCamera; }
const Matrix4& GetCameraToWorld() const restrict(amp, cpu) { return mCameraToWorld; }
const Matrix4& GetProjection() const restrict(amp, cpu) { return mProjection; }
const Matrix4& GetInverseProjection() const restrict(amp, cpu) { return mInverseProjection; }
void RotateCam(float yaw, float pitch)
{
mYaw += yaw;
mPitch = Clamp(mPitch + pitch, 3.14f * -0.45f, 3.14f * 0.45f);
UpdateMatrices();
}
void TranslateCam(const Vector3& trans)
{
mPos += trans;
UpdateMatrices();
}
Ray GenerateRay(int x, int y, int i, StratifiedSampler& samp, amputils::Rng& rng) const restrict(amp, cpu) {
auto camSamp = samp.Sample2D(rng, i);
const float fx = (camSamp.x + float(x)) / float(mFilmWidth);
const float fy = (camSamp.y + float(y)) / float(mFilmHeight);
const Vector4 rayFilm(2.0f * fx - 1.0f, 2.0f * fy - 1.0f, 0.0f, 1.0f);
const Vector4 rayFilmCam = Project(rayFilm, GetInverseProjection());
const Vector4 rayFilmWorld = Transform(rayFilmCam, GetCameraToWorld());
const Vector4 rayStartWorld(mPos.x(), mPos.y(), mPos.z(), 1.0f);
const Vector4 rayDirection(rayFilmWorld - rayStartWorld);
return Ray(Vector3(rayStartWorld.e(0), rayStartWorld.e(1), rayStartWorld.e(2)), Vector3(rayDirection.e(0), rayDirection.e(1), rayDirection.e(2)));
}
private:
void UpdateMatrices()
{
XMMATRIX cam = XMMatrixLookAtLH(XMVectorSet(mPos.x(), mPos.y(), mPos.z(), 1.0f), XMVectorSet(mPos.x(), mPos.y(), mPos.z() + 1.0f, 1.0f), XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f));
XMMATRIX rot = XMMatrixMultiply(XMMatrixRotationY(mYaw), XMMatrixRotationX(mPitch));
mWorldToCamera = XMMatrixMultiply(cam, rot);
mCameraToWorld = XMMatrixInverse(nullptr, mWorldToCamera.GetXMMATRIX());
}
Vector3 mPos;
float mYaw, mPitch;
Matrix4 mWorldToCamera;
Matrix4 mCameraToWorld;
Matrix4 mProjection;
Matrix4 mInverseProjection;
int mFilmWidth, mFilmHeight;
};
class Scene;
class SceneView
{
public:
int GetNumPrims() const restrict(amp, cpu) { return prims.extent.size(); }
const Primitive& GetPrim(int i) const restrict(amp, cpu) { return prims[i]; }
const Camera& GetCamera() const restrict(amp, cpu) { return camera; }
const Primitive& GetLight() const restrict(amp, cpu) { return light; }
private:
friend class Scene;
explicit SceneView(const Camera& camera_, const vector<Primitive>& prims_, const Primitive& light_)
: camera(camera_),
prims(prims_.size(), prims_),
light(light_)
{
}
Camera camera;
array_view<const Primitive> prims;
Primitive light;
};
class Scene
{
public:
explicit Scene(int filmWidth, int filmHeight, const initializer_list<Primitive>& prims_)
: camera(filmWidth, filmHeight),
prims(prims_.begin(), prims_.end()),
light(Sphere(Vector3(50.0f, 50.0f, -50.0f), 5.0f), Vector3(0.75f, 0.75f, 0.67f))
{
}
SceneView GetView() const
{
return SceneView(camera, prims, light);
}
Camera& GetCamera() { return camera; }
const Camera& GetCamera() const { return camera; }
const Primitive& GetLight() const { return light; }
Primitive& GetPrimitive(int i) { return prims[i]; }
private:
Camera camera;
vector<Primitive> prims;
Primitive light;
};
}
Scene MakeScene(int filmWidth, int filmHeight)
{
auto prims = {
Primitive(Sphere(Vector3(0.0f, 0.0f, 1100.0f), 1000.0f), Vector3(0.61f, 0.78f, 1.0f)),
Primitive(Sphere(Vector3(0.0f, -1010.0f, 0.0f), 1000.0f), Vector3(0.85f, 0.85f, 0.85f)),
Primitive(Sphere(Vector3(-2.0f, -6.5f, 2.0f), 4.0f), Vector3(0.85f, 0.85f, 0.85f)),
Primitive(Sphere(Vector3(-2.0f, -1.5f, 2.0f), 3.0f), Vector3(0.85f, 0.85f, 0.85f)),
Primitive(Sphere(Vector3(-2.0f, 2.8f, 2.0f), 2.0f), Vector3(0.85f, 0.85f, 0.85f)),
Primitive(Sphere(Vector3(-2.0f, 2.8f, 0.0f), 0.2f), Vector3(0.05f, 0.05f, 0.05f)),
Primitive(Sphere(Vector3(-2.5f, 3.5f, 0.3f), 0.2f), Vector3(0.05f, 0.05f, 0.05f)),
Primitive(Sphere(Vector3(-1.5f, 3.5f, 0.3f), 0.2f), Vector3(0.05f, 0.05f, 0.05f))
};
return Scene(filmWidth, filmHeight, prims);
}
class App : public IApp {
public:
App(void* hwnd, int width, int height);
~App();
void Update(bool mouseButtons[2], int mouseX, int mouseY) override;
private:
void UpdateControllerState();
void UpdateTexture(unsigned t);
Device mDevice;
// No copy or assignment
App(const App&);
App operator=(const App&);
ID3D11VertexShaderPtr mVs;
ID3D11PixelShaderPtr mPs;
accelerator_view mAcceleratorView;
texture<float_4, 2> mAmpTex;
ID3D11Texture2DPtr mD3dTex;
ID3D11ShaderResourceViewPtr mTexView;
ID3D11SamplerStatePtr mSamplerState;
ID3D11InputLayoutPtr mInputLayout;
ID3D11BufferPtr mQuadVertexBuffer;
ID3D11BufferPtr mQuadIndexBuffer;
chrono::time_point<chrono::high_resolution_clock> mLastFrameTime;
Scene mScene;
XINPUT_STATE mControllerState;
vector<amputils::Rng::SeedValue> mRngSeeds;
};
App::App(void* hwnd, int width, int height) :
mDevice(hwnd, width, height),
mAcceleratorView(create_accelerator_view(mDevice.GetD3D11Device())),
mAmpTex(width, height, mAcceleratorView),
mD3dTex(get_texture(mAmpTex)),
mLastFrameTime(chrono::high_resolution_clock::now()),
mScene(MakeScene(width, height)),
mRngSeeds(mAmpTex.extent.size())
{
auto vs = LoadFile("simplevs.cso");
mDevice.GetD3D11Device()->CreateVertexShader(vs.data(), vs.size(), nullptr, &mVs);
auto ps = LoadFile("simpleps.cso");
mDevice.GetD3D11Device()->CreatePixelShader(ps.data(), ps.size(), nullptr, &mPs);
CD3D11_SHADER_RESOURCE_VIEW_DESC resViewDesc(mD3dTex, D3D11_SRV_DIMENSION_TEXTURE2D);
mDevice.GetD3D11Device()->CreateShaderResourceView(mD3dTex, &resViewDesc, &mTexView);
CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
mDevice.GetD3D11Device()->CreateSamplerState(&samplerDesc, &mSamplerState);
D3D11_INPUT_ELEMENT_DESC elems[] = {
{ "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
mDevice.GetD3D11Device()->CreateInputLayout(elems, ArraySize(elems), vs.data(), vs.size(), &mInputLayout);
SimpleVertex quadVerts[] = {
{ -1.0f, 1.0f, 0.0f },
{ 1.0f, 1.0f, 0.0f },
{ 1.0f, -1.0f, 0.0f },
{ -1.0f, -1.0f, 0.0f },
};
CD3D11_BUFFER_DESC vbDesc(sizeof(quadVerts), D3D11_BIND_VERTEX_BUFFER);
D3D11_SUBRESOURCE_DATA vbData = { quadVerts, 0, 0 };
mDevice.GetD3D11Device()->CreateBuffer(&vbDesc, &vbData, &mQuadVertexBuffer);
uint16_t quadIndices[] = { 0, 1, 2, 2, 3, 0 };
CD3D11_BUFFER_DESC ibDesc(sizeof(quadIndices), D3D11_BIND_INDEX_BUFFER);
D3D11_SUBRESOURCE_DATA ibData = { quadIndices, 0, 0 };
mDevice.GetD3D11Device()->CreateBuffer(&ibDesc, &ibData, &mQuadIndexBuffer);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<unsigned int> dis;
for_each(begin(mRngSeeds), end(mRngSeeds), [&gen, &dis](amputils::Rng::SeedValue& seedValue) {
seedValue = amputils::Rng::SeedValue(dis(gen), dis(gen), dis(gen), dis(gen));
});
amputils::Rng rng(amputils::Rng::SeedValue(dis(gen), dis(gen), dis(gen), dis(gen)));
StratifiedSampler sampler(4);
for (int i = 0; i < sampler.GetNumSamples(); ++i) {
auto samp = sampler.Sample2D(rng, i);
cout << samp.x << ", " << samp.y << "\n";
}
}
App::~App() {
}
bool IsShadowed(const Ray& ray, const SceneView& scene) restrict(amp, cpu)
{
auto lightT = FLT_MAX;
if (!IntersectP(ray, scene.GetLight().GetSphere(), lightT))
return true;
for (auto i = decltype(scene.GetNumPrims())(0); i < scene.GetNumPrims(); ++i)
{
const auto& sphere = scene.GetPrim(i).GetSphere();
float t = FLT_MAX;
if (IntersectP(ray, sphere, t) && t < lightT) {
return true;
}
}
return false;
}
template<int RecursionDepth>
inline Vector3 TraceRay(const Ray& ray, const SceneView& scene, Rng& rng, int sampIdx) restrict(amp, cpu)
{
Vector3 color(0.1f, 0.1f, 0.25f);
auto nearestT = FLT_MAX;
DifferentialGeometry nearestDg;
Vector3 nearestDiffuseColor(0.0f);
for (auto i = decltype(scene.GetNumPrims())(0); i < scene.GetNumPrims(); ++i)
{
const auto& sphere = scene.GetPrim(i).GetSphere();
float t = FLT_MAX;
DifferentialGeometry dg;
if (Intersect(ray, sphere, t, dg) && t < nearestT) {
nearestT = t;
nearestDg = dg;
nearestDiffuseColor = scene.GetPrim(i).GetColor();
}
}
if (nearestT < FLT_MAX) {
// Direct lighting
StratifiedSampler sampler(4);
auto samp = sampler.Sample2D(rng, sampIdx);
Vector3 lightNormal(0.0f);
const auto lightPos = scene.GetLight().GetSphere().Sample(nearestDg.GetPoint(), samp.x, samp.y, lightNormal);
const auto L = Normalize(lightPos - nearestDg.GetPoint());
if (IsShadowed(Ray(nearestDg.GetPoint() + L * 1e-3f, L), scene))
color = Vector3(0.0f);
else
color = nearestDiffuseColor * Dot(nearestDg.GetNormal(), L) * scene.GetLight().GetColor();
// Indirect lighting
auto indirectSamp = sampler.Sample2D(rng, sampIdx);
auto indirectDirLocal = CosineSampleHemisphere(indirectSamp.x, indirectSamp.y);
Vector3 normalZ = nearestDg.GetNormal();
Vector3 normalX(0.0f), normalY(0.0f);
CoordinateSystem(normalZ, normalX, normalY);
auto indirectDirWorld = normalX * indirectDirLocal.x() + normalY * indirectDirLocal.y() + normalZ * indirectDirLocal.z();
Ray indirectRay(nearestDg.GetPoint() + nearestDg.GetNormal() * 1e-3f, indirectDirWorld);
auto indirectColor = TraceRay<RecursionDepth - 1>(indirectRay, scene, rng, sampIdx) * CosineHemispherePdf(Dot(indirectDirWorld, nearestDg.GetNormal()), 0.0f);
color += indirectColor * nearestDiffuseColor;
}
return color;
}
template<>
inline Vector3 TraceRay<0>(const Ray& /*ray*/, const SceneView& /*scene*/, Rng& /*rng*/, int /*sampIdx*/) restrict(amp, cpu)
{
return Vector3(0.0f);
}
const int Height = 512;
const int Width = 512;
inline float_4 RaytraceScene(int x, int y, const SceneView& scene, Rng& rng) restrict (amp, cpu)
{
float_4 totalIrradiance = 0.0f;
StratifiedSampler sampler(4);
for (int samp = 0; samp < sampler.GetNumSamples(); ++samp)
{
auto ray = scene.GetCamera().GenerateRay(x, y, samp, sampler, rng);
totalIrradiance += ToFloat4(TraceRay<4>(ray, scene, rng, samp));
}
totalIrradiance *= 1.0f / sampler.GetNumSamples();
return totalIrradiance;
}
void App::UpdateTexture(unsigned t)
{
auto sceneView = mScene.GetView();
auto ampTexView = writeonly_texture_view<float_4, 2>(mAmpTex);
auto rngSeedsView = array_view<const amputils::Rng::SeedValue, 2>(ampTexView.extent, mRngSeeds);
parallel_for_each(mAcceleratorView, mAmpTex.extent, [ampTexView, sceneView, t, rngSeedsView](index<2> idx) restrict(amp) {
//Rng::SeedValue rngSeed(rngSeedsView[idx].z1 + t, rngSeedsView[idx].z2 + t, rngSeedsView[idx].z3 + t, rngSeedsView[idx].z4 + t);
Rng rng(rngSeedsView[idx]);
ampTexView.set(idx, RaytraceScene(idx[1], idx[0], sceneView, rng));
});
}
void App::UpdateControllerState()
{
for (DWORD i = 0; i < 4; ++i)
{
// Simply get the state of the controller from XInput.
ZeroMemory(&mControllerState, sizeof(mControllerState));
DWORD dwResult = XInputGetState(i, &mControllerState);
if (dwResult == ERROR_SUCCESS)
return;
}
}
float GetThumbstickAmount(const int rawVal)
{
const int inputDeadzone = static_cast<int>(0.24f * float(0x7FFF)); // Default to 24% of the +/- 32767 range.
const int deadzoneVal = abs(rawVal) < inputDeadzone ? 0 : rawVal + inputDeadzone * Sign(rawVal);
return float(deadzoneVal) / float(0x7FFF - inputDeadzone);
}
void App::Update(bool /*mouseButtons*/[2], int /*mouseX*/, int /*mouseY*/)
{
static const auto startTime = chrono::high_resolution_clock::now();
auto frameTime = chrono::high_resolution_clock::now();
auto frameMs = chrono::duration_cast<chrono::milliseconds>(frameTime - mLastFrameTime);
//float frameS = chrono::duration_cast<chrono::milliseconds>(frameTime - startTime).count() * 1e-3f;
mLastFrameTime = frameTime;
UpdateControllerState();
float xRot = GetThumbstickAmount(mControllerState.Gamepad.sThumbRX) * float(frameMs.count()) * -1e-3f;
float yRot = GetThumbstickAmount(mControllerState.Gamepad.sThumbRY) * float(frameMs.count()) * 1e-3f;
mScene.GetCamera().RotateCam(xRot, yRot);
Vector4 camForward = mScene.GetCamera().GetCameraToWorld().GetRow(2);
Vector4 camStrafe = mScene.GetCamera().GetCameraToWorld().GetRow(0);
float forward = GetThumbstickAmount(mControllerState.Gamepad.sThumbLY) * float(frameMs.count()) * 5e-3f;
float strafe = GetThumbstickAmount(mControllerState.Gamepad.sThumbLX) * float(frameMs.count()) * 5e-3f;
Vector4 camTranslate = camForward * forward + camStrafe * strafe;
mScene.GetCamera().TranslateCam(Vector3(camTranslate.x(), camTranslate.y(), camTranslate.z()));
/*
auto movingSpherePos = Vector3(sin(frameS), cos(frameS), cos(frameS)) * 5.0f;
mScene.GetPrimitive(6) = Primitive(Sphere(movingSpherePos, 3.0f), Vector3(0.6f, 0.6f, 0.05f));
*/
UpdateTexture(static_cast<unsigned>(frameTime.time_since_epoch().count()));
mDevice.ClearDefaultRenderTarget();
mDevice.SetDefaultRenderTarget();
ID3D11DeviceContextPtr deviceContext;
mDevice.GetD3D11Device()->GetImmediateContext(&deviceContext);
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
deviceContext->IASetInputLayout(mInputLayout);
deviceContext->IASetIndexBuffer(mQuadIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
ID3D11Buffer* vbs[] = { mQuadVertexBuffer.GetInterfacePtr() };
UINT strides[] = { sizeof(SimpleVertex) };
UINT offsets[] = { 0 };
deviceContext->IASetVertexBuffers(0, ArraySize(vbs), vbs, strides, offsets);
deviceContext->VSSetShader(mVs, nullptr, 0);
deviceContext->PSSetShader(mPs, nullptr, 0);
ID3D11ShaderResourceView* srvs[] = { mTexView.GetInterfacePtr() };
deviceContext->PSSetShaderResources(0, ArraySize(srvs), srvs);
ID3D11SamplerState* samplers[] = { mSamplerState.GetInterfacePtr() };
deviceContext->PSSetSamplers(0, ArraySize(samplers), samplers);
deviceContext->DrawIndexed(6, 0, 0);
ID3D11ShaderResourceView* emptySrvs[] = { nullptr };
deviceContext->PSSetShaderResources(0, ArraySize(emptySrvs), emptySrvs);
mDevice.Present();
cout << frameMs.count() << endl;
}
unique_ptr<IApp> CreateApp(void* hwnd, int width, int height) {
return unique_ptr<IApp>(new App(hwnd, width, height));
}