Demonstrate a possible structure of the WebGPU API
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Oct 2017 23:32:10 +0000 (23:32 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 26 Oct 2017 23:32:10 +0000 (23:32 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178874

Reviewed by Dean Jackson.

Over the past few weeks, we've been putting together an example showing that a WebGPU API
which has implicit barriers can work on all three low-level graphics APIs. We've implemented
it on top of Vulkan first, because this is the API which has the strictest requirements and
is most difficult to use.

With this API, this is a valid WebGPU snippet:

auto device = WebGPU::Device::create(hInstance, hWnd);
auto& commandQueue = device->getCommandQueue();
auto& renderState = device->getRenderState(vertexShader, "main", fragmentShader, "main", { }, { }, { }, nullptr);

… later, in the draw() function …

auto renderPass = commandQueue->createRenderPass(nullptr);
renderPass->setRenderState(renderState);
renderPass->setViewport(0, 0, width, height);
renderPass->setScissorRect(0, 0, width, height);
renderPass->draw(3);
commandQueue->commitRenderPass(std::move(renderPass));
commandQueue->present();

This snippet doesn’t hook up any vertex attributes or resources, which means the vertex
shader has to say something like ({vec4(…), vec4(…), vec4(…)})[gl_VertexIndex]. It also
passes in “nullptr” when creating the render pass, which means “render to the screen, rather
than to a frame buffer.” You can also see that it doesn’t attach any resources to the draw
call.

In Direct3D 12 and Vulkan, resources are bound in sets, rather than individually. For
example, a set might contain two uniform buffers, a texture, and another uniform buffer. At
draw time, you swap in whole sets of resources with a single call. A shader invocation can
access a collection of sets. Because all shader resource accesses are indirected through
these sets, the shape of these sets needs to be supplied at the time you compile the render
state. Here is a snippet which bounds a single set which contains a uniform buffer and a
texture:

auto buffer = device->getBuffer(bufferInitialContents);
auto texture = device->getTexture(buffer width, height, WebGPU::PixelFormat::RGBA8, textureInitialContents);
// One resource set, which holds a single uniform buffer object and a single texture
auto& renderState = device->getRenderState(vertexShader, "main", fragmentShader, "main", { }, { }, { { WebGPU::ResourceType::UniformBufferObject, WebGPU::ResourceType::Texture } }, nullptr);

… later, in the draw() function …

auto renderPass = commandQueue->createRenderPass(nullptr);
renderPass->setRenderState(renderState);
renderPass->setResources(0, { WebGPU::UniformBufferObjectReference(buffer.get()), WebGPU::TextureReference(texture.get()) });

renderPass->draw(3);
commandQueue->commitRenderPass(std::move(renderPass));
commandQueue->present();

The first argument to the setResources() call identifies which set to populate with the supplied resources.

One tenant of the low-level graphics APIs is that, if you’ve enabled double buffering (or
triple buffering), the GPU is executing one frame at the same time you are recording the
next frame. This often means that you need duplicate resources so the CPU and GPU don’t step
on each other’s toes. However, platforms have platform-specific requirements about whether
or not they can be double / triple buffered, and we don’t want to expose this to the Web for
fear of badly-authored programs.

To solve this, resources are reference counted, and the return type of getBuffer() is an
RAII type called BufferHolder which increments and decrements the reference count
automatically. The reference count is also incremented and decremented when the GPU is using
the resource in a Pass. When the reference count reaches 0, the resource isn’t destroyed;
instead, it’s simply moved to a “free list” which getBuffer() may pull from. Therefore,
applications don’t need to know whether the frame buffer is double buffered or triple
buffered; they can just getBuffer() each frame, and the correct number of buffers will be
created and recycled.

{
    auto buffer = device->getBuffer(bufferSize); // These get recycled
    … populate the buffer …
    auto renderPass = commandQueue->createRenderPass(nullptr);
    renderPass->setRenderState(renderState);
    renderPass->setResources(0, { WebGPU::UniformBufferObjectReference(buffer.get()) });
    …
    renderPass->draw(…);
    commandQueue->commitRenderPass(std::move(renderPass));
}
commandQueue->present();

In Direct3D and Vulkan, vertex buffers and index buffers are not part of the resource sets
mentioned above. Instead, you tell the render state about the shape of the vertex and index
buffers, and you swap them out independently in the draw loop. Metal and Vulkan have almost
identical API to specify this shape of the vertex buffers, so I’ve mostly copied it. In this
example, we have two vertex attributes, a vec2 and a vec3, which both come from the same
buffer:

// { Attribute format, offset within stride, buffer to pull from }
std::vector<WebGPU::RenderState::VertexAttribute> vertexAttributes = { {WebGPU::RenderState::VertexFormat::Float2, 0, 0}, {WebGPU::RenderState::VertexFormat::Float3, sizeof(float) * 2, 0} };
// A single vertex buffer, with a stride of 5 floats
auto& renderState = device->getRenderState(vertexShader, "main", fragmentShader, "main", { sizeof(float) * 5 }, vertexAttributes, resourceTypes, nullptr);

… later, in the draw() function …

auto renderPass = commandQueue->createRenderPass(nullptr);
renderPass->setRenderState(renderState);
renderPass->setVertexAttributeBuffers({ vertexBuffer.get() }); // The one vertex buffer which both attributes pull from
renderPass->setResources(…);

renderPass->draw(…);
commandQueue->commitRenderPass(std::move(renderPass));
commandQueue->present();

You can also tell the RenderState about how many render targets you have and their formats,
and then when you create the RenderPass, you specify the specific textures you want to
render into.

std::vector<WebGPU::PixelFormat> colorPixelFormats = { WebGPU::PixelFormat::RGBA8, WebGPU::PixelFormat::RGBA8 }; // Two render targets, with these formats
auto& renderState = device->getRenderState(vertexShader, "main", fragmentShader, "main", vertexBufferStrides, vertexAttributes, resourceTypes, &colorPixelFormats);

… later, in the draw() function …

std::vector<std::reference_wrapper<WebGPU::Texture>> destinationTextures = { texture1->get(), texture2->get() };
auto renderPass = commandQueue->createRenderPass(&destinationTextures);
renderPass->setRenderState(renderState);

renderPass->draw(…);
commandQueue->commitRenderPass(std::move(renderPass));

// Now, draw one of the textures to the screen. Note that no synchronization is necessary here!
auto renderPass = commandQueue->createRenderPass(nullptr);
renderPass->setRenderState(renderState2);
renderPass->setResources(0, { WebGPU:: TextureReference(texture1.get()) });

renderPass->draw(…);
commandQueue->commitRenderPass(std::move(renderPass));
commandQueue->present();

Just like how in Metal has Render Encoders and Compute Encoders, WebGPU has RenderPasses
and ComputePasses.

auto& computeState = device->getComputeState(computeShader, "main", resourceTypes);

auto computePass = commandQueue->createComputePass();
computePass->setComputeState(computeState);
computePass->setResources(0, resources);
computePass->dispatch(width, height, depth);
commandQueue->commitComputePass(std::move(computePass));

// Now, draw the resources we just computed. Note that no synchronization is necessary here!
auto renderPass = commandQueue->createRenderPass(nullptr);
renderPass->setRenderState(renderState);
renderPass->setResources(0, resources });

renderPass->draw(…);
commandQueue->commitRenderPass(std::move(renderPass));
commandQueue->present();

There are also two other types of passes: one that corresponds to a Metal blit encoder, and
one that allows the CPU to change the contents of GPU buffers and textures. This last kind
of pass is a little interesting: you can’t just change the contents of a buffer at any time
you feel like it, because that resource might be in use by the GPU. Therefore, we need to do
the same kind of synchronization that we already do at render pass boundaries.

In addition, both Vulkan and Direct3D have a concept of a memory heap. A resource might
exist inside a heap which is fast, but invisible from the CPU, or in a heap which is slow,
but visible by the CPU. Certain operations are not possible from some types of images (e.g.
non-tiled textures may not be able to be sampled from). The usual way to get around this
problem is to have two resources: a slow staging resource which the CPU can see, and a fast
resource which the CPU can’t see. Uploading data is a two-pass algorithm, where the CPU
memcpy()s into the slow staging resource, and then a blit command is enqueued on the GPU to
copy the contents of the staging resource to the real resource. This requires that the
upload have access to the commandQueue so it can possibly enqueue a blit between the staging
and real resources. Therefore, a pass is the right level of abstraction for these facilities.

std::queue<boost::unique_future<std::vector<uint8_t>>> futureQueue; // Promises for data downloads from the GPU

… later, in the draw() function …

// See if any of the previously-enqueued downloads are finished
while (!futureQueue.empty() && futureQueue.front(). has_value()) {
    std::vector<uint8_t>& data = futureQueue.front().get();
    // Use the downloaded data
    futureQueue.pop();
}

auto hostAccessPass = commandQueue->createHostAccessPass();
hostAccessPass->overwriteBuffer(buffer->get(), bufferContents); // Upload data to a resource

futureQueue.emplace(hostAccessPass->getBufferContents(buffer->get()));
commandQueue->commitHostAccessPass(std::move(hostAccessPass));

You can also issue copy commands between resources entirely on the GPU:

auto blitPass = commandQueue->createBlitPass();
blitPass->copyTexture(source->get(), destination->get(), sourceX, sourceY, destinationX, destinationY, width, height);
commandQueue->commitBlitPass(std::move(blitPass));

* Scripts/webkitpy/style/checker.py:
* WebGPUAPIStructure/Example/Example.cpp: Added.
(readFile):
(drawWebGPU):
(wWinMain):
(MyRegisterClass):
(InitInstance):
(WndProc):
* WebGPUAPIStructure/Example/Example.h: Added.
* WebGPUAPIStructure/Example/Example.ico: Added.
* WebGPUAPIStructure/Example/Example.rc: Added.
* WebGPUAPIStructure/Example/Example.vcxproj: Added.
* WebGPUAPIStructure/Example/Example.vcxproj.filters: Added.
* WebGPUAPIStructure/Example/Example.vcxproj.user: Added.
* WebGPUAPIStructure/Example/resource.h: Added.
* WebGPUAPIStructure/Example/small.ico: Added.
* WebGPUAPIStructure/Example/stdafx.cpp: Added.
* WebGPUAPIStructure/Example/stdafx.h: Added.
* WebGPUAPIStructure/Example/targetver.h: Added.
* WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj: Added.
* WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj.filters: Added.
* WebGPUAPIStructure/WebGPU-Common/WebGPU.cpp: Added.
(WebGPU::BufferHolder::BufferHolder):
(WebGPU::BufferHolder::~BufferHolder):
(WebGPU::TextureHolder::TextureHolder):
(WebGPU::TextureHolder::~TextureHolder):
(WebGPU::SamplerHolder::SamplerHolder):
(WebGPU::SamplerHolder::~SamplerHolder):
* WebGPUAPIStructure/WebGPU-Common/WebGPU.h: Added.
(WebGPU::Queue::~Queue):
(WebGPU::RenderState::~RenderState):
(WebGPU::ComputeState::~ComputeState):
(WebGPU::Buffer::~Buffer):
(WebGPU::Texture::~Texture):
(WebGPU::Sampler::~Sampler):
(WebGPU::TextureReference::TextureReference):
(WebGPU::TextureReference::get const):
(WebGPU::SamplerReference::SamplerReference):
(WebGPU::SamplerReference::get const):
(WebGPU::UniformBufferObjectReference::UniformBufferObjectReference):
(WebGPU::UniformBufferObjectReference::get const):
(WebGPU::ShaderStorageBufferObjectReference::ShaderStorageBufferObjectReference):
(WebGPU::ShaderStorageBufferObjectReference::get const):
(WebGPU::RenderPass::~RenderPass):
(WebGPU::ComputePass::~ComputePass):
(WebGPU::BlitPass::~BlitPass):
(WebGPU::HostAccessPass::~HostAccessPass):
(WebGPU::BufferHolder::get):
(WebGPU::TextureHolder::get):
(WebGPU::SamplerHolder::get):
(WebGPU::Device::~Device):
* WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.cpp: Added.
(WebGPU::BlitPassImpl::BlitPassImpl):
(WebGPU::BlitPassImpl::copyTexture):
* WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.h: Added.
* WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.cpp: Added.
(WebGPU::BufferImpl::BufferImpl):
(WebGPU::BufferImpl::decrementReferenceCount):
* WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.h: Added.
(WebGPU::BufferImpl::getBuffer const):
(WebGPU::BufferImpl::getDeviceMemory const):
(WebGPU::BufferImpl::incrementReferenceCount):
(WebGPU::BufferImpl::getLength const):
* WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.cpp: Added.
(WebGPU::ComputePassImpl::ComputePassImpl):
(WebGPU::ComputePassImpl::setComputeState):
(WebGPU::ComputePassImpl::setResources):
(WebGPU::ComputePassImpl::dispatch):
* WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.h: Added.
* WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.cpp: Added.
(WebGPU::ComputeStateImpl::ComputeStateImpl):
* WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.h: Added.
(WebGPU::ComputeStateImpl::getPipeline const):
(WebGPU::ComputeStateImpl::getPipelineLayout const):
(WebGPU::ComputeStateImpl::getDescriptorSetLayouts const):
* WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.cpp: Added.
(WebGPU::Device::create):
(WebGPU::convertPixelFormat):
(WebGPU::convertFormat):
(WebGPU::debugReport):
(WebGPU::DeviceImpl::DeviceImpl):
(WebGPU::DeviceImpl::getCommandQueue):
(WebGPU::DeviceImpl::prepareShader):
(WebGPU::DeviceImpl::createPipelineLayout):
(WebGPU::DeviceImpl::createCompatibleRenderPass):
(WebGPU::convertVertexFormat):
(WebGPU::DeviceImpl::getRenderState):
(WebGPU::DeviceImpl::getComputeState):
(WebGPU::DeviceImpl::getBuffer):
(WebGPU::DeviceImpl::returnBuffer):
(WebGPU::DeviceImpl::getTexture):
(WebGPU::DeviceImpl::returnTexture):
(WebGPU::DeviceImpl::getSampler):
(WebGPU::DeviceImpl::returnSampler):
(WebGPU::DeviceImpl::~DeviceImpl):
* WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.h: Added.
(WebGPU::DeviceImpl::UniqueDebugReportCallbackEXT::UniqueDebugReportCallbackEXT):
(WebGPU::DeviceImpl::UniqueDebugReportCallbackEXT::operator=):
(WebGPU::DeviceImpl::UniqueDebugReportCallbackEXT::~UniqueDebugReportCallbackEXT):
(WebGPU::DeviceImpl::UniqueDebugReportCallbackEXT::destroy):
(WebGPU::DeviceImpl::TextureParameters::operator== const):
(WebGPU::DeviceImpl::TextureParametersHash::operator() const):
* WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.cpp: Added.
(WebGPU::HostAccessPassImpl::HostAccessPassImpl):
(WebGPU::HostAccessPassImpl::overwriteBuffer):
(WebGPU::HostAccessPassImpl::getBufferContents):
(WebGPU::HostAccessPassImpl::execute):
* WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.h: Added.
(WebGPU::HostAccessPassImpl::getFinishedEvent const):
* WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.cpp: Added.
(WebGPU::PassImpl::PassImpl):
(WebGPU::ResourceVisitor::operator()):
(WebGPU::ResourceVisitor::getBindings const):
(WebGPU::ResourceVisitor::releaseWriteDescriptorSets):
(WebGPU::ResourceVisitor::getDescriptorImageInfos const):
(WebGPU::ResourceVisitor::getDescriptorBufferInfos const):
(WebGPU::ResourceVisitor::getBuffers const):
(WebGPU::ResourceVisitor::getTextures const):
(WebGPU::ResourceVisitor::getSamplers const):
(WebGPU::ResourceVisitor::getImageCount const):
(WebGPU::ResourceVisitor::getSamplerCount const):
(WebGPU::ResourceVisitor::getUniformBufferCount const):
(WebGPU::ResourceVisitor::getStorageBufferCount const):
(WebGPU::PassImpl::setResources):
(WebGPU::PassImpl::insertBuffer):
(WebGPU::PassImpl::insertTexture):
(WebGPU::PassImpl::insertSampler):
* WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.h: Added.
(WebGPU::PassImpl::getCommandBuffer const):
(WebGPU::PassImpl::iterateBuffers):
(WebGPU::PassImpl::iterateTextures):
(WebGPU::PassImpl::ResourceReference::ResourceReference):
(WebGPU::PassImpl::ResourceReference::~ResourceReference):
(WebGPU::PassImpl::ResourceReference::operator=):
(WebGPU::PassImpl::ResourceReference::operator== const):
(WebGPU::PassImpl::ResourceReference::get const):
(WebGPU::PassImpl::ResourceReference::release):
(WebGPU::PassImpl::ResourceReferenceHash::operator() const):
* WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.cpp: Added.
(WebGPU::QueueImpl::QueueImpl):
(WebGPU::QueueImpl::prepareCurrentFrame):
(WebGPU::QueueImpl::createSpecificRenderPass):
(WebGPU::QueueImpl::createFramebuffer):
(WebGPU::QueueImpl::createRenderPass):
(WebGPU::QueueImpl::commitRenderPass):
(WebGPU::QueueImpl::createComputePass):
(WebGPU::QueueImpl::commitComputePass):
(WebGPU::QueueImpl::createBlitPass):
(WebGPU::QueueImpl::commitBlitPass):
(WebGPU::QueueImpl::createHostAccessPass):
(WebGPU::QueueImpl::commitHostAccessPass):
(WebGPU::QueueImpl::present):
(WebGPU::QueueImpl::commitPass):
(WebGPU::QueueImpl::synchronizeResources):
(WebGPU::QueueImpl::~QueueImpl):
* WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.h: Added.
* WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.cpp: Added.
(WebGPU::RenderPassImpl::RenderPassImpl):
(WebGPU::RenderPassImpl::setRenderState):
(WebGPU::RenderPassImpl::setVertexAttributeBuffers):
(WebGPU::RenderPassImpl::setResources):
(WebGPU::RenderPassImpl::setViewport):
(WebGPU::RenderPassImpl::setScissorRect):
(WebGPU::RenderPassImpl::draw):
* WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.h: Added.
* WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.cpp: Added.
(WebGPU::RenderStateImpl::RenderStateImpl):
* WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.h: Added.
(WebGPU::RenderStateImpl::getPipeline const):
(WebGPU::RenderStateImpl::getPipelineLayout const):
(WebGPU::RenderStateImpl::getDescriptorSetLayouts const):
* WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.cpp: Added.
(WebGPU::SamplerImpl::SamplerImpl):
(WebGPU::SamplerImpl::decrementReferenceCount):
* WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.h: Added.
(WebGPU::SamplerImpl::getSampler):
(WebGPU::SamplerImpl::incrementReferenceCount):
(WebGPU::SamplerImpl::getFilter):
(WebGPU::SamplerImpl::getMipmapMode):
(WebGPU::SamplerImpl::getAddressMode):
* WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.cpp: Added.
(WebGPU::TextureImpl::TextureImpl):
(WebGPU::TextureImpl::decrementReferenceCount):
* WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.h: Added.
(WebGPU::TextureImpl::getImage const):
(WebGPU::TextureImpl::getImageView const):
(WebGPU::TextureImpl::getFormat const):
(WebGPU::TextureImpl::incrementReferenceCount):
(WebGPU::TextureImpl::getWidth const):
(WebGPU::TextureImpl::getHeight const):
(WebGPU::TextureImpl::getTransferredToGPU const):
(WebGPU::TextureImpl::setTransferredToGPU):
* WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj: Added.
* WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj.filters: Added.
* WebGPUAPIStructure/WebGPU.sln: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@224065 268f45cc-cd09-0410-ab3c-d52691b4dbfc

45 files changed:
Tools/ChangeLog
Tools/Scripts/webkitpy/style/checker.py
Tools/WebGPUAPIStructure/Example/Example.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/Example.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/Example.ico [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/Example.rc [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/Example.vcxproj [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/Example.vcxproj.filters [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/Example.vcxproj.user [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/resource.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/small.ico [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/stdafx.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/stdafx.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/Example/targetver.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj.filters [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.cpp [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.h [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj.filters [new file with mode: 0644]
Tools/WebGPUAPIStructure/WebGPU.sln [new file with mode: 0644]

index 99bd8e0..367da9b 100644 (file)
@@ -1,3 +1,394 @@
+2017-10-26  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        Demonstrate a possible structure of the WebGPU API
+        https://bugs.webkit.org/show_bug.cgi?id=178874
+
+        Reviewed by Dean Jackson.
+
+        Over the past few weeks, we've been putting together an example showing that a WebGPU API
+        which has implicit barriers can work on all three low-level graphics APIs. We've implemented
+        it on top of Vulkan first, because this is the API which has the strictest requirements and
+        is most difficult to use.
+
+        With this API, this is a valid WebGPU snippet:
+
+        auto device = WebGPU::Device::create(hInstance, hWnd);
+        auto& commandQueue = device->getCommandQueue();
+        auto& renderState = device->getRenderState(vertexShader, "main", fragmentShader, "main", { }, { }, { }, nullptr);
+        
+        … later, in the draw() function …
+        
+        auto renderPass = commandQueue->createRenderPass(nullptr);
+        renderPass->setRenderState(renderState);
+        renderPass->setViewport(0, 0, width, height);
+        renderPass->setScissorRect(0, 0, width, height);
+        renderPass->draw(3);
+        commandQueue->commitRenderPass(std::move(renderPass));
+        commandQueue->present();
+
+        This snippet doesn’t hook up any vertex attributes or resources, which means the vertex
+        shader has to say something like ({vec4(…), vec4(…), vec4(…)})[gl_VertexIndex]. It also
+        passes in “nullptr” when creating the render pass, which means “render to the screen, rather
+        than to a frame buffer.” You can also see that it doesn’t attach any resources to the draw
+        call.
+
+        In Direct3D 12 and Vulkan, resources are bound in sets, rather than individually. For
+        example, a set might contain two uniform buffers, a texture, and another uniform buffer. At
+        draw time, you swap in whole sets of resources with a single call. A shader invocation can
+        access a collection of sets. Because all shader resource accesses are indirected through
+        these sets, the shape of these sets needs to be supplied at the time you compile the render
+        state. Here is a snippet which bounds a single set which contains a uniform buffer and a
+        texture:
+
+        auto buffer = device->getBuffer(bufferInitialContents);
+        auto texture = device->getTexture(buffer width, height, WebGPU::PixelFormat::RGBA8, textureInitialContents);
+        // One resource set, which holds a single uniform buffer object and a single texture
+        auto& renderState = device->getRenderState(vertexShader, "main", fragmentShader, "main", { }, { }, { { WebGPU::ResourceType::UniformBufferObject, WebGPU::ResourceType::Texture } }, nullptr);
+        
+        … later, in the draw() function …
+        
+        auto renderPass = commandQueue->createRenderPass(nullptr);
+        renderPass->setRenderState(renderState);
+        renderPass->setResources(0, { WebGPU::UniformBufferObjectReference(buffer.get()), WebGPU::TextureReference(texture.get()) });
+        …
+        renderPass->draw(3);
+        commandQueue->commitRenderPass(std::move(renderPass));
+        commandQueue->present();
+        
+        The first argument to the setResources() call identifies which set to populate with the supplied resources.
+
+        One tenant of the low-level graphics APIs is that, if you’ve enabled double buffering (or
+        triple buffering), the GPU is executing one frame at the same time you are recording the
+        next frame. This often means that you need duplicate resources so the CPU and GPU don’t step
+        on each other’s toes. However, platforms have platform-specific requirements about whether
+        or not they can be double / triple buffered, and we don’t want to expose this to the Web for
+        fear of badly-authored programs.
+
+        To solve this, resources are reference counted, and the return type of getBuffer() is an
+        RAII type called BufferHolder which increments and decrements the reference count
+        automatically. The reference count is also incremented and decremented when the GPU is using
+        the resource in a Pass. When the reference count reaches 0, the resource isn’t destroyed;
+        instead, it’s simply moved to a “free list” which getBuffer() may pull from. Therefore,
+        applications don’t need to know whether the frame buffer is double buffered or triple
+        buffered; they can just getBuffer() each frame, and the correct number of buffers will be
+        created and recycled.
+        
+        {
+            auto buffer = device->getBuffer(bufferSize); // These get recycled
+            … populate the buffer …
+            auto renderPass = commandQueue->createRenderPass(nullptr);
+            renderPass->setRenderState(renderState);
+            renderPass->setResources(0, { WebGPU::UniformBufferObjectReference(buffer.get()) });
+            …
+            renderPass->draw(…);
+            commandQueue->commitRenderPass(std::move(renderPass));
+        }
+        commandQueue->present();
+
+        In Direct3D and Vulkan, vertex buffers and index buffers are not part of the resource sets
+        mentioned above. Instead, you tell the render state about the shape of the vertex and index
+        buffers, and you swap them out independently in the draw loop. Metal and Vulkan have almost
+        identical API to specify this shape of the vertex buffers, so I’ve mostly copied it. In this
+        example, we have two vertex attributes, a vec2 and a vec3, which both come from the same
+        buffer:
+        
+        // { Attribute format, offset within stride, buffer to pull from }
+        std::vector<WebGPU::RenderState::VertexAttribute> vertexAttributes = { {WebGPU::RenderState::VertexFormat::Float2, 0, 0}, {WebGPU::RenderState::VertexFormat::Float3, sizeof(float) * 2, 0} };
+        // A single vertex buffer, with a stride of 5 floats
+        auto& renderState = device->getRenderState(vertexShader, "main", fragmentShader, "main", { sizeof(float) * 5 }, vertexAttributes, resourceTypes, nullptr);
+        
+        … later, in the draw() function …
+        
+        auto renderPass = commandQueue->createRenderPass(nullptr);
+        renderPass->setRenderState(renderState);
+        renderPass->setVertexAttributeBuffers({ vertexBuffer.get() }); // The one vertex buffer which both attributes pull from
+        renderPass->setResources(…);
+        …
+        renderPass->draw(…);
+        commandQueue->commitRenderPass(std::move(renderPass));
+        commandQueue->present();
+
+        You can also tell the RenderState about how many render targets you have and their formats,
+        and then when you create the RenderPass, you specify the specific textures you want to
+        render into.
+
+        std::vector<WebGPU::PixelFormat> colorPixelFormats = { WebGPU::PixelFormat::RGBA8, WebGPU::PixelFormat::RGBA8 }; // Two render targets, with these formats
+        auto& renderState = device->getRenderState(vertexShader, "main", fragmentShader, "main", vertexBufferStrides, vertexAttributes, resourceTypes, &colorPixelFormats);
+        
+        … later, in the draw() function …
+        
+        std::vector<std::reference_wrapper<WebGPU::Texture>> destinationTextures = { texture1->get(), texture2->get() };
+        auto renderPass = commandQueue->createRenderPass(&destinationTextures);
+        renderPass->setRenderState(renderState);
+        …
+        renderPass->draw(…);
+        commandQueue->commitRenderPass(std::move(renderPass));
+        
+        // Now, draw one of the textures to the screen. Note that no synchronization is necessary here!
+        auto renderPass = commandQueue->createRenderPass(nullptr);
+        renderPass->setRenderState(renderState2);
+        renderPass->setResources(0, { WebGPU:: TextureReference(texture1.get()) });
+        …
+        renderPass->draw(…);
+        commandQueue->commitRenderPass(std::move(renderPass));
+        commandQueue->present();
+
+        Just like how in Metal has Render Encoders and Compute Encoders, WebGPU has RenderPasses
+        and ComputePasses.
+
+        auto& computeState = device->getComputeState(computeShader, "main", resourceTypes);
+        …
+        auto computePass = commandQueue->createComputePass();
+        computePass->setComputeState(computeState);
+        computePass->setResources(0, resources);
+        computePass->dispatch(width, height, depth);
+        commandQueue->commitComputePass(std::move(computePass));
+        
+        // Now, draw the resources we just computed. Note that no synchronization is necessary here!
+        auto renderPass = commandQueue->createRenderPass(nullptr);
+        renderPass->setRenderState(renderState);
+        renderPass->setResources(0, resources });
+        …
+        renderPass->draw(…);
+        commandQueue->commitRenderPass(std::move(renderPass));
+        commandQueue->present();
+
+        There are also two other types of passes: one that corresponds to a Metal blit encoder, and
+        one that allows the CPU to change the contents of GPU buffers and textures. This last kind
+        of pass is a little interesting: you can’t just change the contents of a buffer at any time
+        you feel like it, because that resource might be in use by the GPU. Therefore, we need to do
+        the same kind of synchronization that we already do at render pass boundaries.
+
+        In addition, both Vulkan and Direct3D have a concept of a memory heap. A resource might
+        exist inside a heap which is fast, but invisible from the CPU, or in a heap which is slow,
+        but visible by the CPU. Certain operations are not possible from some types of images (e.g.
+        non-tiled textures may not be able to be sampled from). The usual way to get around this
+        problem is to have two resources: a slow staging resource which the CPU can see, and a fast
+        resource which the CPU can’t see. Uploading data is a two-pass algorithm, where the CPU
+        memcpy()s into the slow staging resource, and then a blit command is enqueued on the GPU to
+        copy the contents of the staging resource to the real resource. This requires that the
+        upload have access to the commandQueue so it can possibly enqueue a blit between the staging
+        and real resources. Therefore, a pass is the right level of abstraction for these facilities.
+
+        std::queue<boost::unique_future<std::vector<uint8_t>>> futureQueue; // Promises for data downloads from the GPU
+        
+        … later, in the draw() function …
+        
+        // See if any of the previously-enqueued downloads are finished
+        while (!futureQueue.empty() && futureQueue.front(). has_value()) {
+            std::vector<uint8_t>& data = futureQueue.front().get();
+            // Use the downloaded data
+            futureQueue.pop();
+        }
+        …
+        auto hostAccessPass = commandQueue->createHostAccessPass();
+        hostAccessPass->overwriteBuffer(buffer->get(), bufferContents); // Upload data to a resource
+        
+        futureQueue.emplace(hostAccessPass->getBufferContents(buffer->get()));
+        commandQueue->commitHostAccessPass(std::move(hostAccessPass));
+        
+        You can also issue copy commands between resources entirely on the GPU:
+        
+        auto blitPass = commandQueue->createBlitPass();
+        blitPass->copyTexture(source->get(), destination->get(), sourceX, sourceY, destinationX, destinationY, width, height);
+        commandQueue->commitBlitPass(std::move(blitPass));
+
+        * Scripts/webkitpy/style/checker.py:
+        * WebGPUAPIStructure/Example/Example.cpp: Added.
+        (readFile):
+        (drawWebGPU):
+        (wWinMain):
+        (MyRegisterClass):
+        (InitInstance):
+        (WndProc):
+        * WebGPUAPIStructure/Example/Example.h: Added.
+        * WebGPUAPIStructure/Example/Example.ico: Added.
+        * WebGPUAPIStructure/Example/Example.rc: Added.
+        * WebGPUAPIStructure/Example/Example.vcxproj: Added.
+        * WebGPUAPIStructure/Example/Example.vcxproj.filters: Added.
+        * WebGPUAPIStructure/Example/Example.vcxproj.user: Added.
+        * WebGPUAPIStructure/Example/resource.h: Added.
+        * WebGPUAPIStructure/Example/small.ico: Added.
+        * WebGPUAPIStructure/Example/stdafx.cpp: Added.
+        * WebGPUAPIStructure/Example/stdafx.h: Added.
+        * WebGPUAPIStructure/Example/targetver.h: Added.
+        * WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj: Added.
+        * WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj.filters: Added.
+        * WebGPUAPIStructure/WebGPU-Common/WebGPU.cpp: Added.
+        (WebGPU::BufferHolder::BufferHolder):
+        (WebGPU::BufferHolder::~BufferHolder):
+        (WebGPU::TextureHolder::TextureHolder):
+        (WebGPU::TextureHolder::~TextureHolder):
+        (WebGPU::SamplerHolder::SamplerHolder):
+        (WebGPU::SamplerHolder::~SamplerHolder):
+        * WebGPUAPIStructure/WebGPU-Common/WebGPU.h: Added.
+        (WebGPU::Queue::~Queue):
+        (WebGPU::RenderState::~RenderState):
+        (WebGPU::ComputeState::~ComputeState):
+        (WebGPU::Buffer::~Buffer):
+        (WebGPU::Texture::~Texture):
+        (WebGPU::Sampler::~Sampler):
+        (WebGPU::TextureReference::TextureReference):
+        (WebGPU::TextureReference::get const):
+        (WebGPU::SamplerReference::SamplerReference):
+        (WebGPU::SamplerReference::get const):
+        (WebGPU::UniformBufferObjectReference::UniformBufferObjectReference):
+        (WebGPU::UniformBufferObjectReference::get const):
+        (WebGPU::ShaderStorageBufferObjectReference::ShaderStorageBufferObjectReference):
+        (WebGPU::ShaderStorageBufferObjectReference::get const):
+        (WebGPU::RenderPass::~RenderPass):
+        (WebGPU::ComputePass::~ComputePass):
+        (WebGPU::BlitPass::~BlitPass):
+        (WebGPU::HostAccessPass::~HostAccessPass):
+        (WebGPU::BufferHolder::get):
+        (WebGPU::TextureHolder::get):
+        (WebGPU::SamplerHolder::get):
+        (WebGPU::Device::~Device):
+        * WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.cpp: Added.
+        (WebGPU::BlitPassImpl::BlitPassImpl):
+        (WebGPU::BlitPassImpl::copyTexture):
+        * WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.h: Added.
+        * WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.cpp: Added.
+        (WebGPU::BufferImpl::BufferImpl):
+        (WebGPU::BufferImpl::decrementReferenceCount):
+        * WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.h: Added.
+        (WebGPU::BufferImpl::getBuffer const):
+        (WebGPU::BufferImpl::getDeviceMemory const):
+        (WebGPU::BufferImpl::incrementReferenceCount):
+        (WebGPU::BufferImpl::getLength const):
+        * WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.cpp: Added.
+        (WebGPU::ComputePassImpl::ComputePassImpl):
+        (WebGPU::ComputePassImpl::setComputeState):
+        (WebGPU::ComputePassImpl::setResources):
+        (WebGPU::ComputePassImpl::dispatch):
+        * WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.h: Added.
+        * WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.cpp: Added.
+        (WebGPU::ComputeStateImpl::ComputeStateImpl):
+        * WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.h: Added.
+        (WebGPU::ComputeStateImpl::getPipeline const):
+        (WebGPU::ComputeStateImpl::getPipelineLayout const):
+        (WebGPU::ComputeStateImpl::getDescriptorSetLayouts const):
+        * WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.cpp: Added.
+        (WebGPU::Device::create):
+        (WebGPU::convertPixelFormat):
+        (WebGPU::convertFormat):
+        (WebGPU::debugReport):
+        (WebGPU::DeviceImpl::DeviceImpl):
+        (WebGPU::DeviceImpl::getCommandQueue):
+        (WebGPU::DeviceImpl::prepareShader):
+        (WebGPU::DeviceImpl::createPipelineLayout):
+        (WebGPU::DeviceImpl::createCompatibleRenderPass):
+        (WebGPU::convertVertexFormat):
+        (WebGPU::DeviceImpl::getRenderState):
+        (WebGPU::DeviceImpl::getComputeState):
+        (WebGPU::DeviceImpl::getBuffer):
+        (WebGPU::DeviceImpl::returnBuffer):
+        (WebGPU::DeviceImpl::getTexture):
+        (WebGPU::DeviceImpl::returnTexture):
+        (WebGPU::DeviceImpl::getSampler):
+        (WebGPU::DeviceImpl::returnSampler):
+        (WebGPU::DeviceImpl::~DeviceImpl):
+        * WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.h: Added.
+        (WebGPU::DeviceImpl::UniqueDebugReportCallbackEXT::UniqueDebugReportCallbackEXT):
+        (WebGPU::DeviceImpl::UniqueDebugReportCallbackEXT::operator=):
+        (WebGPU::DeviceImpl::UniqueDebugReportCallbackEXT::~UniqueDebugReportCallbackEXT):
+        (WebGPU::DeviceImpl::UniqueDebugReportCallbackEXT::destroy):
+        (WebGPU::DeviceImpl::TextureParameters::operator== const):
+        (WebGPU::DeviceImpl::TextureParametersHash::operator() const):
+        * WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.cpp: Added.
+        (WebGPU::HostAccessPassImpl::HostAccessPassImpl):
+        (WebGPU::HostAccessPassImpl::overwriteBuffer):
+        (WebGPU::HostAccessPassImpl::getBufferContents):
+        (WebGPU::HostAccessPassImpl::execute):
+        * WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.h: Added.
+        (WebGPU::HostAccessPassImpl::getFinishedEvent const):
+        * WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.cpp: Added.
+        (WebGPU::PassImpl::PassImpl):
+        (WebGPU::ResourceVisitor::operator()):
+        (WebGPU::ResourceVisitor::getBindings const):
+        (WebGPU::ResourceVisitor::releaseWriteDescriptorSets):
+        (WebGPU::ResourceVisitor::getDescriptorImageInfos const):
+        (WebGPU::ResourceVisitor::getDescriptorBufferInfos const):
+        (WebGPU::ResourceVisitor::getBuffers const):
+        (WebGPU::ResourceVisitor::getTextures const):
+        (WebGPU::ResourceVisitor::getSamplers const):
+        (WebGPU::ResourceVisitor::getImageCount const):
+        (WebGPU::ResourceVisitor::getSamplerCount const):
+        (WebGPU::ResourceVisitor::getUniformBufferCount const):
+        (WebGPU::ResourceVisitor::getStorageBufferCount const):
+        (WebGPU::PassImpl::setResources):
+        (WebGPU::PassImpl::insertBuffer):
+        (WebGPU::PassImpl::insertTexture):
+        (WebGPU::PassImpl::insertSampler):
+        * WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.h: Added.
+        (WebGPU::PassImpl::getCommandBuffer const):
+        (WebGPU::PassImpl::iterateBuffers):
+        (WebGPU::PassImpl::iterateTextures):
+        (WebGPU::PassImpl::ResourceReference::ResourceReference):
+        (WebGPU::PassImpl::ResourceReference::~ResourceReference):
+        (WebGPU::PassImpl::ResourceReference::operator=):
+        (WebGPU::PassImpl::ResourceReference::operator== const):
+        (WebGPU::PassImpl::ResourceReference::get const):
+        (WebGPU::PassImpl::ResourceReference::release):
+        (WebGPU::PassImpl::ResourceReferenceHash::operator() const):
+        * WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.cpp: Added.
+        (WebGPU::QueueImpl::QueueImpl):
+        (WebGPU::QueueImpl::prepareCurrentFrame):
+        (WebGPU::QueueImpl::createSpecificRenderPass):
+        (WebGPU::QueueImpl::createFramebuffer):
+        (WebGPU::QueueImpl::createRenderPass):
+        (WebGPU::QueueImpl::commitRenderPass):
+        (WebGPU::QueueImpl::createComputePass):
+        (WebGPU::QueueImpl::commitComputePass):
+        (WebGPU::QueueImpl::createBlitPass):
+        (WebGPU::QueueImpl::commitBlitPass):
+        (WebGPU::QueueImpl::createHostAccessPass):
+        (WebGPU::QueueImpl::commitHostAccessPass):
+        (WebGPU::QueueImpl::present):
+        (WebGPU::QueueImpl::commitPass):
+        (WebGPU::QueueImpl::synchronizeResources):
+        (WebGPU::QueueImpl::~QueueImpl):
+        * WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.h: Added.
+        * WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.cpp: Added.
+        (WebGPU::RenderPassImpl::RenderPassImpl):
+        (WebGPU::RenderPassImpl::setRenderState):
+        (WebGPU::RenderPassImpl::setVertexAttributeBuffers):
+        (WebGPU::RenderPassImpl::setResources):
+        (WebGPU::RenderPassImpl::setViewport):
+        (WebGPU::RenderPassImpl::setScissorRect):
+        (WebGPU::RenderPassImpl::draw):
+        * WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.h: Added.
+        * WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.cpp: Added.
+        (WebGPU::RenderStateImpl::RenderStateImpl):
+        * WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.h: Added.
+        (WebGPU::RenderStateImpl::getPipeline const):
+        (WebGPU::RenderStateImpl::getPipelineLayout const):
+        (WebGPU::RenderStateImpl::getDescriptorSetLayouts const):
+        * WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.cpp: Added.
+        (WebGPU::SamplerImpl::SamplerImpl):
+        (WebGPU::SamplerImpl::decrementReferenceCount):
+        * WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.h: Added.
+        (WebGPU::SamplerImpl::getSampler):
+        (WebGPU::SamplerImpl::incrementReferenceCount):
+        (WebGPU::SamplerImpl::getFilter):
+        (WebGPU::SamplerImpl::getMipmapMode):
+        (WebGPU::SamplerImpl::getAddressMode):
+        * WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.cpp: Added.
+        (WebGPU::TextureImpl::TextureImpl):
+        (WebGPU::TextureImpl::decrementReferenceCount):
+        * WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.h: Added.
+        (WebGPU::TextureImpl::getImage const):
+        (WebGPU::TextureImpl::getImageView const):
+        (WebGPU::TextureImpl::getFormat const):
+        (WebGPU::TextureImpl::incrementReferenceCount):
+        (WebGPU::TextureImpl::getWidth const):
+        (WebGPU::TextureImpl::getHeight const):
+        (WebGPU::TextureImpl::getTransferredToGPU const):
+        (WebGPU::TextureImpl::setTransferredToGPU):
+        * WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj: Added.
+        * WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj.filters: Added.
+        * WebGPUAPIStructure/WebGPU.sln: Added.
+
 2017-10-26  Eric Carlson  <eric.carlson@apple.com>
 
         NowPlayingInfo should contain a unique identifier
index b84194d..ca8f0fe 100644 (file)
@@ -213,7 +213,8 @@ _PATH_RULES_SPECIFIER = [
     #
     ([os.path.join('webkitpy', 'thirdparty'),
       os.path.join('Source', 'ThirdParty', 'ANGLE'),
-      os.path.join('Source', 'ThirdParty', 'xdgmime')],
+      os.path.join('Source', 'ThirdParty', 'xdgmime'),
+      os.path.join('Tools', 'WebGPUAPIStructure')],
      ["-",
       "+pep8/W191",  # Tabs
       "+pep8/W291",  # Trailing white space
diff --git a/Tools/WebGPUAPIStructure/Example/Example.cpp b/Tools/WebGPUAPIStructure/Example/Example.cpp
new file mode 100644 (file)
index 0000000..a15cedf
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// Example.cpp : Defines the entry point for the application.
+//
+
+#include "stdafx.h"
+#include "Example.h"
+#include <vector>
+#include <cstdio>
+#include <cassert>
+#include <fstream>
+#include <queue>
+#include <boost/optional.hpp>
+#include <WebGPU.h>
+
+#define MAX_LOADSTRING 100
+
+// Global Variables:
+HINSTANCE hInst;                                // current instance
+WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
+WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name
+
+HWND hWnd;
+std::unique_ptr<WebGPU::Device> device;
+WebGPU::Queue* commandQueue = nullptr;
+WebGPU::RenderState* renderState = nullptr;
+WebGPU::RenderState* renderStateRTT = nullptr;
+WebGPU::ComputeState* computeState = nullptr;
+std::unique_ptr<WebGPU::BufferHolder> buffer;
+std::unique_ptr<WebGPU::TextureHolder> texture;
+std::unique_ptr<WebGPU::BufferHolder> vertexBuffer;
+std::unique_ptr<WebGPU::BufferHolder> vertexBufferRTT;
+std::unique_ptr<WebGPU::SamplerHolder> sampler;
+std::queue<boost::unique_future<std::vector<uint8_t>>> futureQueue;
+bool initializedBuffers = false;
+float t = 0;
+
+// Forward declarations of functions included in this code module:
+ATOM                MyRegisterClass(HINSTANCE hInstance);
+BOOL                InitInstance(HINSTANCE, int);
+LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
+
+static std::vector<uint8_t> readFile(const std::string& filename) {
+    std::ifstream file(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
+    assert(file.is_open());
+    auto size = file.tellg();
+    std::vector<uint8_t> result(size);
+    file.seekg(0, std::ios::beg);
+    file.read(reinterpret_cast<char*>(result.data()), size);
+    assert(file.tellg() == size);
+    return result;
+}
+
+static void drawWebGPU() {
+    if (!commandQueue || !renderState || !buffer || !vertexBuffer || !vertexBufferRTT || !renderStateRTT || !texture)
+        return;
+
+    WINDOWINFO windowInfo;
+    auto success = GetWindowInfo(hWnd, &windowInfo);
+    assert(success);
+    auto width = windowInfo.rcClient.right - windowInfo.rcClient.left;
+    auto height = windowInfo.rcClient.bottom - windowInfo.rcClient.top;
+
+    while (!futureQueue.empty() && futureQueue.front().is_ready() && futureQueue.front().has_value()) {
+        const auto& data = futureQueue.front().get();
+        float t = *reinterpret_cast<const float*>(data.data());
+        std::ostringstream ss;
+        ss << "T has value " << t << std::endl;
+        OutputDebugStringA(ss.str().c_str());
+        futureQueue.pop();
+    }
+
+    if (!initializedBuffers) {
+        auto hostAccessPass = commandQueue->createHostAccessPass();
+
+        std::vector<uint8_t> bufferContents(sizeof(float));
+        for (std::size_t i = 0; i < sizeof(float); ++i)
+            bufferContents[i] = reinterpret_cast<uint8_t*>(&t)[i];
+        hostAccessPass->overwriteBuffer(buffer->get(), bufferContents);
+
+        std::vector<float> vertexBufferContents = { -1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1 };
+        std::vector<uint8_t> vertexBufferRawContents(sizeof(float) * 12);
+        for (std::size_t i = 0; i < vertexBufferContents.size(); ++i) {
+            float value = vertexBufferContents[i];
+            for (std::size_t j = 0; j < sizeof(float); ++j)
+                vertexBufferRawContents[sizeof(float) * i + j] = reinterpret_cast<uint8_t*>(&value)[j];
+        }
+        hostAccessPass->overwriteBuffer(vertexBuffer->get(), vertexBufferRawContents);
+
+        std::vector<float> vertexBufferContentsRTT = { -0.5, 0.5, 0, -0.5, 0.5, 1.0 };
+        std::vector<uint8_t> vertexBufferRawContentsRTT(sizeof(float) * 6);
+        for (std::size_t i = 0; i < vertexBufferContentsRTT.size(); ++i) {
+            float value = vertexBufferContentsRTT[i];
+            for (std::size_t j = 0; j < sizeof(float); ++j)
+                vertexBufferRawContentsRTT[sizeof(float) * i + j] = reinterpret_cast<uint8_t*>(&value)[j];
+        }
+        hostAccessPass->overwriteBuffer(vertexBufferRTT->get(), vertexBufferRawContentsRTT);
+        commandQueue->commitHostAccessPass(std::move(hostAccessPass));
+        initializedBuffers = true;
+    }
+
+    auto computePass = commandQueue->createComputePass();
+    computePass->setComputeState(*computeState);
+    computePass->setResources(0, { WebGPU::ShaderStorageBufferObjectReference(buffer->get()) });
+    computePass->dispatch(1, 1, 1);
+    commandQueue->commitComputePass(std::move(computePass));
+
+    std::vector<std::reference_wrapper<WebGPU::Texture>> rtt = { texture->get() };
+    auto renderPass = commandQueue->createRenderPass(&rtt);
+    renderPass->setRenderState(*renderStateRTT);
+    renderPass->setVertexAttributeBuffers({ vertexBufferRTT->get() });
+    renderPass->setResources(0, { WebGPU::UniformBufferObjectReference(buffer->get()) });
+    renderPass->setViewport(0, 0, width, height);
+    renderPass->setScissorRect(0, 0, width, height);
+    renderPass->draw(3);
+    commandQueue->commitRenderPass(std::move(renderPass));
+
+    auto renderPass2 = commandQueue->createRenderPass(nullptr);
+    renderPass2->setRenderState(*renderState);
+    renderPass2->setVertexAttributeBuffers({ vertexBuffer->get() });
+    renderPass2->setResources(0, { WebGPU::UniformBufferObjectReference(buffer->get()), WebGPU::TextureReference(texture->get()), WebGPU::SamplerReference(sampler->get()) });
+    renderPass2->setViewport(0, 0, width, height);
+    renderPass2->setScissorRect(0, 0, width, height);
+    renderPass2->draw(6);
+    commandQueue->commitRenderPass(std::move(renderPass2));
+
+    auto hostAccessPass = commandQueue->createHostAccessPass();
+    futureQueue.emplace(hostAccessPass->getBufferContents(buffer->get()));
+    commandQueue->commitHostAccessPass(std::move(hostAccessPass));
+
+    commandQueue->present();
+}
+
+int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
+                     _In_opt_ HINSTANCE hPrevInstance,
+                     _In_ LPWSTR    lpCmdLine,
+                     _In_ int       nCmdShow)
+{
+    UNREFERENCED_PARAMETER(hPrevInstance);
+    UNREFERENCED_PARAMETER(lpCmdLine);
+
+    // TODO: Place code here.
+
+    // Initialize global strings
+    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
+    LoadStringW(hInstance, IDC_EXAMPLE, szWindowClass, MAX_LOADSTRING);
+    MyRegisterClass(hInstance);
+
+    // Perform application initialization:
+    if (!InitInstance (hInstance, nCmdShow))
+    {
+        return FALSE;
+    }
+
+    /*
+    #version 460
+
+    layout(location = 0) in vec2 position;
+
+    layout(std140, binding = 0) uniform buf {
+        float t;
+    };
+
+    void main() {
+        float time = t / 10;
+        vec2 delta = vec2(cos(time), sin(time));
+        delta = delta * 0.5;
+        gl_Position = vec4(position + delta, 0.5, 1);
+    }
+    glslangValidator -g -o "C:\Users\lithe\Documents\VertexShader.spv" -V -H "C:\Users\lithe\Documents\VertexShader.vert" -S vert
+    #version 460
+
+    layout (location = 0) out vec4 fragColor;
+
+    void main() {
+        fragColor = vec4(1, 0, 0, 1);
+    }
+    glslangValidator -g -o "C:\Users\lithe\Documents\FragmentShader.spv" -V -H "C:\Users\lithe\Documents\FragmentShader.frag" -S frag
+    #version 460
+
+    layout(location = 0) in vec2 position;
+
+    layout(location = 0) out vec2 texCoord;
+
+    layout(std140, binding = 0) uniform buf {
+        float t;
+    };
+
+    void main() {
+        texCoord = position;
+        gl_Position = vec4(position, 0.5, 1);
+    }
+    glslangValidator -g -o "C:\Users\lithe\Documents\VertexShader2.spv" -V -H "C:\Users\lithe\Documents\VertexShader2.vert" -S vert
+    struct Result {
+        float4 color : SV_Target0;
+    };
+
+    Texture2D<float4> inputTexture : register(t1);
+    SamplerState inputSampler : register(s2);
+
+    Result main(float2 texCoord : TEXCOORD0)
+    {
+        Result result;
+        float4 sampleResult = inputTexture.Sample(inputSampler, texCoord);
+        result.color = float4(sampleResult.rgb, 1.0f);
+        return result;
+    }
+    dxc.exe -spirv -T ps_6_0 "C:\Users\lithe\Documents\PixelShader.hlsl" -Fo "C:\Users\lithe\Documents\FragmentShader2.spv"
+    */
+    commandQueue = &device->getCommandQueue();
+    auto vertexShaderRTT = readFile("C:\\Users\\lithe\\Documents\\VertexShader.spv");
+    auto fragmentShaderRTT = readFile("C:\\Users\\lithe\\Documents\\FragmentShader.spv");
+    auto computeShader = readFile("C:\\Users\\lithe\\Documents\\ComputeShader.spv");
+    auto vertexShader = readFile("C:\\Users\\lithe\\Documents\\VertexShader2.spv");
+    auto fragmentShader = readFile("C:\\Users\\lithe\\Documents\\FragmentShader2.spv");
+
+    vertexBufferRTT = std::unique_ptr<WebGPU::BufferHolder>(new WebGPU::BufferHolder(device->getBuffer(static_cast<unsigned int>(sizeof(float) * 6))));
+    buffer = std::unique_ptr<WebGPU::BufferHolder>(new WebGPU::BufferHolder(device->getBuffer(static_cast<unsigned int>(sizeof(float)))));
+    vertexBuffer = std::unique_ptr<WebGPU::BufferHolder>(new WebGPU::BufferHolder(device->getBuffer(static_cast<unsigned int>(sizeof(float) * 12))));
+
+    sampler = std::unique_ptr<WebGPU::SamplerHolder>(new WebGPU::SamplerHolder(device->getSampler(WebGPU::AddressMode::Repeat, WebGPU::Filter::Linear)));
+
+    WINDOWINFO windowInfo;
+    auto success = GetWindowInfo(hWnd, &windowInfo);
+    assert(success);
+    texture = std::unique_ptr<WebGPU::TextureHolder>(new WebGPU::TextureHolder(device->getTexture(windowInfo.rcClient.right - windowInfo.rcClient.left, windowInfo.rcClient.bottom - windowInfo.rcClient.top, WebGPU::PixelFormat::RGBA8)));
+
+    std::vector<WebGPU::RenderState::VertexAttribute> vertexAttributes = { {WebGPU::RenderState::VertexFormat::Float2, 0, 0} };
+    std::vector<WebGPU::PixelFormat> colorPixelFormats = { WebGPU::PixelFormat::RGBA8 };
+    renderStateRTT = &device->getRenderState(vertexShaderRTT, "main", fragmentShaderRTT, "main", { 8 }, vertexAttributes, { { WebGPU::ResourceType::UniformBufferObject } }, &colorPixelFormats);
+    renderState = &device->getRenderState(vertexShader, "main", fragmentShader, "main", { 8 }, vertexAttributes, { { WebGPU::ResourceType::UniformBufferObject, WebGPU::ResourceType::Texture, WebGPU::ResourceType::Sampler } }, nullptr);
+    computeState = &device->getComputeState(computeShader, "main", { { WebGPU::ResourceType::ShaderStorageBufferObject } });
+    drawWebGPU();
+
+    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_EXAMPLE));
+
+    MSG msg;
+
+    // Main message loop:
+    while (GetMessage(&msg, nullptr, 0, 0))
+    {
+        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
+        {
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+        }
+        RedrawWindow(hWnd, nullptr, nullptr, RDW_INTERNALPAINT);
+    }
+    return (int) msg.wParam;
+}
+
+
+
+//
+//  FUNCTION: MyRegisterClass()
+//
+//  PURPOSE: Registers the window class.
+//
+ATOM MyRegisterClass(HINSTANCE hInstance)
+{
+    WNDCLASSEXW wcex;
+
+    wcex.cbSize = sizeof(WNDCLASSEX);
+
+    wcex.style          = CS_HREDRAW | CS_VREDRAW;
+    wcex.lpfnWndProc    = WndProc;
+    wcex.cbClsExtra     = 0;
+    wcex.cbWndExtra     = 0;
+    wcex.hInstance      = hInstance;
+    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_EXAMPLE));
+    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
+    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
+    wcex.lpszMenuName   = nullptr;
+    wcex.lpszClassName  = szWindowClass;
+    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
+
+    return RegisterClassExW(&wcex);
+}
+
+//
+//   FUNCTION: InitInstance(HINSTANCE, int)
+//
+//   PURPOSE: Saves instance handle and creates main window
+//
+//   COMMENTS:
+//
+//        In this function, we save the instance handle in a global variable and
+//        create and display the main program window.
+//
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
+{
+   hInst = hInstance; // Store instance handle in our global variable
+
+   hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
+      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
+
+   if (!hWnd)
+   {
+      return FALSE;
+   }
+
+   device = WebGPU::Device::create(hInstance, hWnd);
+
+   ShowWindow(hWnd, nCmdShow);
+   UpdateWindow(hWnd);
+
+   return TRUE;
+}
+
+//
+//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
+//
+//  PURPOSE:  Processes messages for the main window.
+//
+//  WM_COMMAND  - process the application menu
+//  WM_PAINT    - Paint the main window
+//  WM_DESTROY  - post a quit message and return
+//
+//
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    switch (message)
+    {
+    case WM_PAINT:
+        drawWebGPU();
+        break;
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        break;
+    default:
+        return DefWindowProc(hWnd, message, wParam, lParam);
+    }
+    return 0;
+}
diff --git a/Tools/WebGPUAPIStructure/Example/Example.h b/Tools/WebGPUAPIStructure/Example/Example.h
new file mode 100644 (file)
index 0000000..d00d47e
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+#include "resource.h"
diff --git a/Tools/WebGPUAPIStructure/Example/Example.ico b/Tools/WebGPUAPIStructure/Example/Example.ico
new file mode 100644 (file)
index 0000000..b3ec03b
Binary files /dev/null and b/Tools/WebGPUAPIStructure/Example/Example.ico differ
diff --git a/Tools/WebGPUAPIStructure/Example/Example.rc b/Tools/WebGPUAPIStructure/Example/Example.rc
new file mode 100644 (file)
index 0000000..df64b42
Binary files /dev/null and b/Tools/WebGPUAPIStructure/Example/Example.rc differ
diff --git a/Tools/WebGPUAPIStructure/Example/Example.vcxproj b/Tools/WebGPUAPIStructure/Example/Example.vcxproj
new file mode 100644 (file)
index 0000000..c17e0d9
--- /dev/null
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>15.0</VCProjectVersion>
+    <ProjectGuid>{5817155C-AB74-445D-9A61-47CDE42C2BAD}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>Example</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>C:\Users\lithe\Documents\Visual Studio 2017\Projects\WebGPU\WebGPU;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>C:\Users\lithe\Documents\Visual Studio 2017\Projects\WebGPU\Debug\WebGPU.lib;C:\Users\lithe\Source\Repos\Vulkan-LoaderAndValidationLayers\install\loader\Debug\vulkan-1.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;BOOST_ALL_DYN_LINK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\WebGPU-Common;C:\Users\lithe\Documents\Visual Studio 2017\Projects\WebGPU\WebGPU;C:\Users\lithe\Documents\src\boost_1_65_1\prefix\include\boost-1_65_1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>C:\Users\lithe\Source\Repos\Vulkan-LoaderAndValidationLayers\install\loader\Debug\vulkan-1.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\Users\lithe\Documents\src\boost_1_65_1\prefix\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="Example.h" />
+    <ClInclude Include="resource.h" />
+    <ClInclude Include="stdafx.h" />
+    <ClInclude Include="targetver.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="Example.cpp" />
+    <ClCompile Include="stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="Example.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="Example.ico" />
+    <Image Include="small.ico" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\WebGPU-Vulkan\WebGPU-Vulkan.vcxproj">
+      <Project>{9b2efa45-d6f5-436b-8dfa-a675d1fecd5d}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/Example/Example.vcxproj.filters b/Tools/WebGPUAPIStructure/Example/Example.vcxproj.filters
new file mode 100644 (file)
index 0000000..7c73cd3
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="stdafx.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="targetver.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="resource.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="Example.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="stdafx.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="Example.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="Example.rc">
+      <Filter>Resource Files</Filter>
+    </ResourceCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="small.ico">
+      <Filter>Resource Files</Filter>
+    </Image>
+    <Image Include="Example.ico">
+      <Filter>Resource Files</Filter>
+    </Image>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/Example/Example.vcxproj.user b/Tools/WebGPUAPIStructure/Example/Example.vcxproj.user
new file mode 100644 (file)
index 0000000..9f31529
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LocalDebuggerEnvironment>VK_LOADER_DEBUG=all
+PATH=C:\Users\lithe\Documents\src\boost_1_65_1\prefix\lib
+$(LocalDebuggerEnvironment)</LocalDebuggerEnvironment>
+    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/Example/resource.h b/Tools/WebGPUAPIStructure/Example/resource.h
new file mode 100644 (file)
index 0000000..916e590
--- /dev/null
@@ -0,0 +1,31 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Example.rc
+//
+
+#define IDS_APP_TITLE 103
+
+#define IDR_MAINFRAME 128
+#define IDD_EXAMPLE_DIALOG 102
+#define IDD_ABOUTBOX 103
+#define IDM_ABOUT 104
+#define IDM_EXIT 105
+#define IDI_EXAMPLE 107
+#define IDI_SMALL 108
+#define IDC_EXAMPLE 109
+#define IDC_MYICON 2
+#ifndef IDC_STATIC
+#define IDC_STATIC -1
+#endif
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+
+#define _APS_NO_MFC 130
+#define _APS_NEXT_RESOURCE_VALUE 129
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/Tools/WebGPUAPIStructure/Example/small.ico b/Tools/WebGPUAPIStructure/Example/small.ico
new file mode 100644 (file)
index 0000000..b3ec03b
Binary files /dev/null and b/Tools/WebGPUAPIStructure/Example/small.ico differ
diff --git a/Tools/WebGPUAPIStructure/Example/stdafx.cpp b/Tools/WebGPUAPIStructure/Example/stdafx.cpp
new file mode 100644 (file)
index 0000000..3904815
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// stdafx.cpp : source file that includes just the standard includes
+// Example.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/Tools/WebGPUAPIStructure/Example/stdafx.h b/Tools/WebGPUAPIStructure/Example/stdafx.h
new file mode 100644 (file)
index 0000000..bb80440
--- /dev/null
@@ -0,0 +1,21 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
+// Windows Header Files:
+#include <windows.h>
+
+// C RunTime Header Files
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include <tchar.h>
+
+
+// TODO: reference additional headers your program requires here
diff --git a/Tools/WebGPUAPIStructure/Example/targetver.h b/Tools/WebGPUAPIStructure/Example/targetver.h
new file mode 100644 (file)
index 0000000..87c0086
--- /dev/null
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj b/Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj
new file mode 100644 (file)
index 0000000..a11a7c2
--- /dev/null
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>15.0</VCProjectVersion>
+    <ProjectGuid>{9E765542-8C51-43B4-B0C0-C166D21CB222}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>WebGPUCommon</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_LIB;BOOST_ALL_DYN_LINK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>C:\Users\lithe\Documents\src\boost_1_65_1\prefix\include\boost-1_65_1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="WebGPU.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="WebGPU.cpp" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj.filters b/Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU-Common.vcxproj.filters
new file mode 100644 (file)
index 0000000..559a6dc
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="WebGPU.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="WebGPU.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU.cpp b/Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU.cpp
new file mode 100644 (file)
index 0000000..ed3a2e0
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "WebGPU.h"
+#include <memory>
+#include <vector>
+#include <cassert>
+
+namespace WebGPU {
+    BufferHolder::BufferHolder(Device& device, Buffer& buffer) : device(device), buffer(&buffer) {
+    }
+
+    BufferHolder::~BufferHolder() {
+        if (buffer)
+            device.returnBuffer(*buffer);
+    }
+
+    BufferHolder::BufferHolder(BufferHolder&& other) : device(other.device), buffer(other.buffer) {
+        other.buffer = nullptr;
+    }
+
+    TextureHolder::TextureHolder(Device& device, Texture& texture) : device(device), texture(&texture) {
+    }
+
+    TextureHolder::~TextureHolder() {
+        if (texture)
+            device.returnTexture(*texture);
+    }
+
+    TextureHolder::TextureHolder(TextureHolder&& other) : device(device), texture(texture) {
+        other.texture = nullptr;
+    }
+
+    SamplerHolder::SamplerHolder(Device& device, Sampler& sampler) : device(device), sampler(&sampler) {
+    }
+
+    SamplerHolder::~SamplerHolder() {
+        if (sampler)
+            device.returnSampler(*sampler);
+    }
+
+    SamplerHolder::SamplerHolder(SamplerHolder&& other) : device(device), sampler(sampler) {
+        other.sampler = nullptr;
+    }
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU.h b/Tools/WebGPUAPIStructure/WebGPU-Common/WebGPU.h
new file mode 100644 (file)
index 0000000..ba59d1d
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <boost/variant.hpp>
+#include <boost/thread.hpp>
+#include <windows.h>
+
+namespace WebGPU {
+    class Texture;
+    class Resource;
+    class Device;
+    class RenderPass;
+    class ComputePass;
+    class BlitPass;
+    class HostAccessPass;
+
+    class Queue {
+    public:
+        virtual ~Queue() {}
+
+        virtual std::unique_ptr<RenderPass> createRenderPass(const std::vector<std::reference_wrapper<Texture>>* textures) = 0;
+        virtual void commitRenderPass(std::unique_ptr<RenderPass>&&) = 0;
+        virtual std::unique_ptr<ComputePass> createComputePass() = 0;
+        virtual void commitComputePass(std::unique_ptr<ComputePass>&&) = 0;
+        virtual std::unique_ptr<BlitPass> createBlitPass() = 0;
+        virtual void commitBlitPass(std::unique_ptr<BlitPass>&&) = 0;
+        virtual std::unique_ptr<HostAccessPass> createHostAccessPass() = 0;
+        virtual void commitHostAccessPass(std::unique_ptr<HostAccessPass>&&) = 0;
+        virtual void present() = 0;
+
+    protected:
+        Queue() = default;
+        Queue(const Queue&) = delete;
+        Queue(Queue&&) = delete;
+        virtual Queue& operator=(const Queue&) = default;
+        virtual Queue& operator=(Queue&&) = default;
+    };
+
+    class RenderState {
+    public:
+        virtual ~RenderState() {}
+
+        enum class VertexFormat {
+            Float,
+            Float2,
+            Float3,
+            Float4
+        };
+
+        struct VertexAttribute {
+            VertexFormat format;
+            unsigned int offsetWithinStride;
+            unsigned int vertexAttributeBufferIndex;
+        };
+
+    protected:
+        RenderState() = default;
+        RenderState(const RenderState&) = delete;
+        RenderState(RenderState&&) = default;
+        virtual RenderState& operator=(const RenderState&) = delete;
+        virtual RenderState& operator=(RenderState&&) = default;
+    };
+
+    class ComputeState {
+    public:
+        virtual ~ComputeState() {}
+
+    protected:
+        ComputeState() = default;
+        ComputeState(const ComputeState&) = delete;
+        ComputeState(ComputeState&&) = default;
+        virtual ComputeState& operator=(const ComputeState&) = delete;
+        virtual ComputeState& operator=(ComputeState&&) = default;
+    };
+
+    enum class ResourceType {
+        Texture,
+        Sampler,
+        UniformBufferObject,
+        ShaderStorageBufferObject,
+    };
+
+    class Buffer {
+    public:
+        virtual ~Buffer() {}
+
+    protected:
+        Buffer() = default;
+        Buffer(const Buffer&) = delete;
+        Buffer(Buffer&&) = default;
+        virtual Buffer& operator=(const Buffer&) = delete;
+        virtual Buffer& operator=(Buffer&&) = default;
+    };
+
+    enum class PixelFormat {
+        RGBA8sRGB,
+        RGBA8
+    };
+
+    class Texture {
+    public:
+        virtual ~Texture() {}
+
+    protected:
+        Texture() = default;
+        Texture(const Texture&) = delete;
+        Texture(Texture&&) = default;
+        virtual Texture& operator=(const Texture&) = delete;
+        virtual Texture& operator=(Texture&&) = default;
+    };
+
+    class Sampler {
+    public:
+        virtual ~Sampler() {}
+
+    protected:
+        Sampler() = default;
+        Sampler(const Sampler&) = delete;
+        Sampler(Sampler&&) = default;
+        virtual Sampler& operator=(const Sampler&) = delete;
+        virtual Sampler& operator=(Sampler&&) = default;
+    };
+
+    class TextureReference {
+    public:
+        TextureReference() = default;
+        TextureReference(Texture& texture) : texture(texture) {}
+        TextureReference(const TextureReference&) = default;
+        TextureReference(TextureReference&&) = default;
+        TextureReference& operator=(const TextureReference&) = default;
+        TextureReference& operator=(TextureReference&&) = default;
+
+        Texture& get() const { return texture; }
+
+    private:
+        Texture& texture;
+    };
+
+    class SamplerReference {
+    public:
+        SamplerReference() = default;
+        SamplerReference(Sampler& sampler) : sampler(sampler) {}
+        SamplerReference(const SamplerReference&) = default;
+        SamplerReference(SamplerReference&&) = default;
+        SamplerReference& operator=(const SamplerReference&) = default;
+        SamplerReference& operator=(SamplerReference&&) = default;
+
+        Sampler& get() const { return sampler; }
+
+    private:
+        Sampler& sampler;
+    };
+
+    class UniformBufferObjectReference {
+    public:
+        UniformBufferObjectReference() = default;
+        UniformBufferObjectReference(Buffer& buffer) : buffer(buffer) {}
+        UniformBufferObjectReference(const UniformBufferObjectReference&) = default;
+        UniformBufferObjectReference(UniformBufferObjectReference&&) = default;
+        UniformBufferObjectReference& operator=(const UniformBufferObjectReference&) = default;
+        UniformBufferObjectReference& operator=(UniformBufferObjectReference&&) = default;
+
+        Buffer& get() const { return buffer; }
+
+    private:
+        Buffer& buffer;
+    };
+
+    class ShaderStorageBufferObjectReference {
+    public:
+        ShaderStorageBufferObjectReference() = default;
+        ShaderStorageBufferObjectReference(Buffer& buffer) : buffer(buffer) {}
+        ShaderStorageBufferObjectReference(const ShaderStorageBufferObjectReference&) = default;
+        ShaderStorageBufferObjectReference(ShaderStorageBufferObjectReference&&) = default;
+        ShaderStorageBufferObjectReference& operator=(const ShaderStorageBufferObjectReference&) = default;
+        ShaderStorageBufferObjectReference& operator=(ShaderStorageBufferObjectReference&&) = default;
+
+        Buffer& get() const { return buffer; }
+
+    private:
+        Buffer& buffer;
+    };
+
+    typedef boost::variant<TextureReference, SamplerReference, UniformBufferObjectReference, ShaderStorageBufferObjectReference> ResourceReference;
+
+    class RenderPass {
+    public:
+        virtual ~RenderPass() {}
+
+        virtual void setRenderState(const RenderState&) = 0;
+        virtual void setVertexAttributeBuffers(const std::vector<std::reference_wrapper<Buffer>>& buffers) = 0;
+        virtual void setResources(unsigned int index, const std::vector<ResourceReference>&) = 0;
+        virtual void setViewport(unsigned int x, unsigned int y, unsigned int width, unsigned int height) = 0;
+        virtual void setScissorRect(unsigned int x, unsigned int y, unsigned int width, unsigned int height) = 0;
+        virtual void draw(unsigned int vertexCount) = 0;
+
+    protected:
+        RenderPass() = default;
+        RenderPass(const RenderPass&) = delete;
+        RenderPass(RenderPass&&) = delete;
+        virtual RenderPass& operator=(const RenderPass&) = default;
+        virtual RenderPass& operator=(RenderPass&&) = default;
+    };
+
+    class ComputePass {
+    public:
+        virtual ~ComputePass() {}
+
+        virtual void setComputeState(const ComputeState&) = 0;
+        virtual void setResources(unsigned int index, const std::vector<ResourceReference>&) = 0;
+        virtual void dispatch(unsigned int width, unsigned int height, unsigned int depth) = 0;
+
+    protected:
+        ComputePass() = default;
+        ComputePass(const ComputePass&) = delete;
+        ComputePass(ComputePass&&) = delete;
+        virtual ComputePass& operator=(const ComputePass&) = default;
+        virtual ComputePass& operator=(ComputePass&&) = default;
+    };
+
+    class BlitPass {
+    public:
+        virtual ~BlitPass() {}
+
+        virtual void copyTexture(Texture& source, Texture& destination, unsigned int sourceX, unsigned int sourceY, unsigned int destinationX, unsigned int destinationY, unsigned int width, unsigned int height) = 0;
+
+    protected:
+        BlitPass() = default;
+        BlitPass(const BlitPass&) = delete;
+        BlitPass(BlitPass&&) = delete;
+        virtual BlitPass& operator=(const BlitPass&) = default;
+        virtual BlitPass& operator=(BlitPass&&) = default;
+    };
+
+    class HostAccessPass {
+    public:
+        virtual ~HostAccessPass() {}
+
+        virtual void overwriteBuffer(Buffer& buffer, const std::vector<uint8_t>& newData) = 0;
+        virtual boost::unique_future<std::vector<uint8_t>> getBufferContents(Buffer& buffer) = 0;
+
+    protected:
+        HostAccessPass() = default;
+        HostAccessPass(const HostAccessPass&) = delete;
+        HostAccessPass(HostAccessPass&&) = delete;
+        virtual HostAccessPass& operator=(const HostAccessPass&) = default;
+        virtual HostAccessPass& operator=(HostAccessPass&&) = default;
+    };
+
+    class BufferHolder {
+    public:
+        BufferHolder() = delete;
+        BufferHolder(Device& device, Buffer& buffer);
+        ~BufferHolder();
+        BufferHolder(const BufferHolder&) = delete;
+        BufferHolder(BufferHolder&&);
+        BufferHolder& operator=(const BufferHolder&) = delete;
+        BufferHolder& operator=(BufferHolder&&) = delete;
+
+        Buffer& get() { return *buffer; }
+
+    private:
+        friend class Device;
+        Device& device;
+        Buffer* buffer;
+    };
+
+    class TextureHolder {
+    public:
+        TextureHolder() = delete;
+        TextureHolder(Device& device, Texture& texture);
+        ~TextureHolder();
+        TextureHolder(const TextureHolder&) = delete;
+        TextureHolder(TextureHolder&&);
+        TextureHolder& operator=(const TextureHolder&) = delete;
+        TextureHolder& operator=(TextureHolder&&) = default;
+
+        Texture& get() { return *texture; }
+
+    private:
+        friend class Device;
+        Device& device;
+        Texture* texture;
+    };
+
+    class SamplerHolder {
+    public:
+        SamplerHolder() = delete;
+        SamplerHolder(Device& device, Sampler& texture);
+        ~SamplerHolder();
+        SamplerHolder(const TextureHolder&) = delete;
+        SamplerHolder(SamplerHolder&&);
+        SamplerHolder& operator=(const SamplerHolder&) = delete;
+        SamplerHolder& operator=(SamplerHolder&&) = default;
+
+        Sampler& get() { return *sampler; }
+
+    private:
+        friend class Device;
+        Device& device;
+        Sampler* sampler;
+    };
+
+    enum class AddressMode {
+        ClampToEdge,
+        MirrorClampToEdge,
+        Repeat,
+        MirrorRepeat
+    };
+
+    enum class Filter {
+        Nearest,
+        Linear
+    };
+
+    class Device {
+    public:
+        static std::unique_ptr<Device> create(HINSTANCE, HWND); // FIXME: This should probably return a shared_ptr.
+
+        virtual ~Device() {}
+
+        virtual Queue& getCommandQueue() = 0;
+        virtual RenderState& getRenderState(const std::vector<uint8_t>& vertexShader, const std::string& vertexShaderName, const std::vector<uint8_t>& fragmentShader, const std::string& fragmentShaderName, const std::vector<unsigned int>& vertexAttributeBufferStrides, const std::vector<RenderState::VertexAttribute>& vertexAttributes, const std::vector<std::vector<ResourceType>>& resources, const std::vector<PixelFormat>* colorPixelFormats) = 0;
+        virtual ComputeState& getComputeState(const std::vector<uint8_t>& shader, const std::string& shaderName, const std::vector<std::vector<ResourceType>>& resources) = 0;
+        virtual BufferHolder getBuffer(unsigned int length) = 0;
+        virtual TextureHolder getTexture(unsigned int width, unsigned int height, PixelFormat format) = 0;
+        virtual SamplerHolder getSampler(AddressMode addressMode, Filter filter) = 0;
+
+    protected:
+        Device() = default;
+        Device(const Device&) = delete;
+        Device(Device&&) = delete;
+        virtual Device& operator=(const Device&) = default;
+        virtual Device& operator=(Device&&) = default;
+
+    private:
+        friend class BufferHolder;
+        friend class TextureHolder;
+        friend class SamplerHolder;
+        virtual void returnBuffer(Buffer&) = 0;
+        virtual void returnTexture(Texture&) = 0;
+        virtual void returnSampler(Sampler&) = 0;
+    };
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.cpp
new file mode 100644 (file)
index 0000000..9fcfde4
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "BlitPassImpl.h"
+
+namespace WebGPU {
+    BlitPassImpl::BlitPassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer) : PassImpl(device, std::move(commandBuffer)) {
+    }
+
+    void BlitPassImpl::copyTexture(Texture& source, Texture& destination, unsigned int sourceX, unsigned int sourceY, unsigned int destinationX, unsigned int destinationY, unsigned int width, unsigned int height) {
+        auto* downcast = dynamic_cast<TextureImpl*>(&source);
+        assert(downcast != nullptr);
+        auto& src = *downcast;
+        downcast = dynamic_cast<TextureImpl*>(&destination);
+        assert(downcast != nullptr);
+        auto& dst = *downcast;
+        const auto subresource = vk::ImageSubresourceLayers()
+            .setAspectMask(vk::ImageAspectFlagBits::eColor)
+            .setMipLevel(0)
+            .setBaseArrayLayer(0)
+            .setLayerCount(1);
+
+        const auto imageCopy = vk::ImageCopy()
+            .setSrcSubresource(subresource)
+            .setSrcOffset(vk::Offset3D(sourceX, sourceY, 0))
+            .setDstSubresource(subresource)
+            .setDstOffset(vk::Offset3D(destinationX, destinationY, 0))
+            .setExtent(vk::Extent3D(width, height, 1));
+        commandBuffer->copyImage(src.getImage(), vk::ImageLayout::eGeneral, dst.getImage(), vk::ImageLayout::eGeneral, { imageCopy });
+
+        insertTexture(src);
+        insertTexture(dst);
+    }
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/BlitPassImpl.h
new file mode 100644 (file)
index 0000000..e58221a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include "PassImpl.h"
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class BlitPassImpl : public PassImpl, public BlitPass {
+    public:
+        BlitPassImpl() = default;
+        virtual ~BlitPassImpl() = default;
+        BlitPassImpl(const BlitPassImpl&) = delete;
+        BlitPassImpl(BlitPassImpl&&) = default;
+        BlitPassImpl& operator=(const BlitPassImpl&) = delete;
+        BlitPassImpl& operator=(BlitPassImpl&&) = default;
+
+        BlitPassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer);
+
+    private:
+        void copyTexture(Texture& source, Texture& destination, unsigned int sourceX, unsigned int sourceY, unsigned int destinationX, unsigned int destinationY, unsigned int width, unsigned int height) override;
+    };
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.cpp
new file mode 100644 (file)
index 0000000..69f58ca
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "BufferImpl.h"
+#include <cstring>
+
+namespace WebGPU {
+    BufferImpl::BufferImpl(std::function<void(BufferImpl&)>&& returnToDevice, unsigned int length, vk::Device device, vk::UniqueDeviceMemory&& memory, vk::UniqueBuffer&& buffer) : returnToDevice(std::move(returnToDevice)), device(device), memory(std::move(memory)), buffer(std::move(buffer)), length(length) {
+    }
+
+    void BufferImpl::decrementReferenceCount() {
+        if (--referenceCount == 0)
+            returnToDevice(*this);
+    }
+}
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/BufferImpl.h
new file mode 100644 (file)
index 0000000..c51d9ed
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include <functional>
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class BufferImpl : public Buffer {
+    public:
+        BufferImpl() = default;
+        virtual ~BufferImpl() = default;
+        BufferImpl(const BufferImpl&) = delete;
+        BufferImpl(BufferImpl&&) = default;
+        BufferImpl& operator=(const BufferImpl&) = delete;
+        BufferImpl& operator=(BufferImpl&&) = default;
+
+        BufferImpl(std::function<void(BufferImpl&)>&& returnToDevice, unsigned int length, vk::Device device, vk::UniqueDeviceMemory&& memory, vk::UniqueBuffer&& buffer);
+
+        vk::Buffer getBuffer() const { return *buffer; }
+        vk::DeviceMemory getDeviceMemory() const { return *memory; }
+        void incrementReferenceCount() { ++referenceCount; }
+        void decrementReferenceCount();
+        unsigned getLength() const { return length; }
+
+    private:
+        std::function<void(BufferImpl&)> returnToDevice;
+        vk::Device device;
+        vk::UniqueDeviceMemory memory;
+        vk::UniqueBuffer buffer;
+        unsigned int length;
+        unsigned int referenceCount{ 0 };
+    };
+}
+
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.cpp
new file mode 100644 (file)
index 0000000..35c44a9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "ComputePassImpl.h"
+#include "ComputeStateImpl.h"
+
+namespace WebGPU {
+    ComputePassImpl::ComputePassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer) : PassImpl(device, std::move(commandBuffer)) {
+    }
+
+    void ComputePassImpl::setComputeState(const ComputeState& computeState) {
+        auto* downcast = dynamic_cast<const ComputeStateImpl*>(&computeState);
+        assert(downcast != nullptr);
+        auto& state = *downcast;
+        currentPipelineLayout = state.getPipelineLayout();
+        commandBuffer->bindPipeline(vk::PipelineBindPoint::eCompute, state.getPipeline());
+    }
+
+    void ComputePassImpl::setResources(unsigned int index, const std::vector<WebGPU::ResourceReference>& resourceReferences) {
+        PassImpl::setResources(vk::PipelineBindPoint::eCompute, index, resourceReferences);
+    }
+
+    void ComputePassImpl::dispatch(unsigned int width, unsigned int height, unsigned int depth) {
+        commandBuffer->dispatch(width, height, depth);
+    }
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputePassImpl.h
new file mode 100644 (file)
index 0000000..d1408d9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include "PassImpl.h"
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class ComputePassImpl : public PassImpl, public ComputePass {
+    public:
+        ComputePassImpl() = default;
+        virtual ~ComputePassImpl() = default;
+        ComputePassImpl(const ComputePassImpl&) = delete;
+        ComputePassImpl(ComputePassImpl&&) = default;
+        ComputePassImpl& operator=(const ComputePassImpl&) = delete;
+        ComputePassImpl& operator=(ComputePassImpl&&) = default;
+
+        ComputePassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer);
+
+    private:
+        void setComputeState(const ComputeState&) override;
+        void setResources(unsigned int index, const std::vector<WebGPU::ResourceReference>&) override;
+        void dispatch(unsigned int width, unsigned int height, unsigned int depth) override;
+    };
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.cpp
new file mode 100644 (file)
index 0000000..d6b2bb4
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "ComputeStateImpl.h"
+
+namespace WebGPU {
+    ComputeStateImpl::ComputeStateImpl(vk::UniquePipeline&& pipeline, std::vector<vk::UniqueDescriptorSetLayout>&& descriptorSetLayouts, vk::UniquePipelineLayout&& pipelineLayout) : pipeline(std::move(pipeline)), descriptorSetLayouts(std::move(descriptorSetLayouts)), pipelineLayout(std::move(pipelineLayout)) {
+    }
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/ComputeStateImpl.h
new file mode 100644 (file)
index 0000000..3ea270a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class ComputeStateImpl : public ComputeState {
+    public:
+        ComputeStateImpl() = default;
+        virtual ~ComputeStateImpl() = default;
+        ComputeStateImpl(const ComputeStateImpl&) = delete;
+        ComputeStateImpl(ComputeStateImpl&&) = default;
+        ComputeStateImpl& operator=(const ComputeStateImpl&) = delete;
+        ComputeStateImpl& operator=(ComputeStateImpl&&) = default;
+
+        ComputeStateImpl(vk::UniquePipeline&& pipeline, std::vector<vk::UniqueDescriptorSetLayout>&& descriptorSetLayouts, vk::UniquePipelineLayout&& pipelineLayout);
+
+        vk::Pipeline getPipeline() const { return *pipeline; }
+        vk::PipelineLayout getPipelineLayout() const { return *pipelineLayout; }
+        const std::vector<vk::UniqueDescriptorSetLayout>& getDescriptorSetLayouts() const { return descriptorSetLayouts; }
+
+    private:
+        vk::UniquePipeline pipeline;
+        std::vector<vk::UniqueDescriptorSetLayout> descriptorSetLayouts;
+        vk::UniquePipelineLayout pipelineLayout;
+    };
+}
+
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.cpp
new file mode 100644 (file)
index 0000000..38dda1f
--- /dev/null
@@ -0,0 +1,618 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "DeviceImpl.h"
+#include "QueueImpl.h"
+#include "RenderStateImpl.h"
+#include "ComputeStateImpl.h"
+#include "QueueImpl.h"
+#include <cassert>
+#include <limits>
+#include <sstream>
+
+namespace WebGPU {
+    static const vk::Format framebufferColorPixelFormat = vk::Format::eR8G8B8A8Srgb;
+
+    std::unique_ptr<Device> Device::create(HINSTANCE hInstance, HWND hWnd) {
+        return std::unique_ptr<Device>(new DeviceImpl(hInstance, hWnd));
+    }
+
+    static vk::Format convertPixelFormat(PixelFormat pixelFormat) {
+        switch (pixelFormat) {
+        case PixelFormat::RGBA8sRGB:
+            return vk::Format::eR8G8B8A8Srgb;
+        case PixelFormat::RGBA8:
+            return vk::Format::eR8G8B8A8Unorm;
+        default:
+            assert(false);
+            return vk::Format::eR8G8B8A8Unorm;
+        }
+    }
+
+    static PixelFormat convertFormat(vk::Format format) {
+        switch (format) {
+        case vk::Format::eR8G8B8A8Srgb:
+            return PixelFormat::RGBA8sRGB;
+        case vk::Format::eR8G8B8A8Unorm:
+            return PixelFormat::RGBA8;
+        default:
+            assert(false);
+            return PixelFormat::RGBA8;
+        }
+    }
+
+    static VkBool32 debugReport(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) {
+        vk::DebugReportFlagsEXT flagsObject = static_cast<vk::DebugReportFlagBitsEXT>(flags);
+        vk::DebugReportObjectTypeEXT objectTypeObject = static_cast<vk::DebugReportObjectTypeEXT>(objectType);
+        std::ostringstream ss;
+        ss << "Vulkan Log: " << vk::to_string(flagsObject) << " " << vk::to_string(objectTypeObject) << " " << object << " " << location << " " << messageCode << " " << std::string(pLayerPrefix) << " " << std::string(pMessage) << std::endl;
+        OutputDebugStringA(ss.str().c_str());
+        return VK_FALSE;
+    }
+
+    DeviceImpl::DeviceImpl(HINSTANCE hInstance, HWND hWnd) {
+        auto layerProperties = vk::enumerateInstanceLayerProperties();
+        auto const app = vk::ApplicationInfo()
+            .setPApplicationName("WebGPU")
+            .setApplicationVersion(0)
+            .setPEngineName("WebGPU")
+            .setEngineVersion(0)
+            .setApiVersion(VK_API_VERSION_1_0);
+        const char* const instanceValidationLayers[] = { "VK_LAYER_LUNARG_standard_validation", "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", "VK_LAYER_GOOGLE_unique_objects" };
+        const char* const instanceExtensionNames[] = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_EXTENSION_NAME };
+        const auto debugReportCallbackCreateInfo = vk::DebugReportCallbackCreateInfoEXT()
+            .setFlags(vk::DebugReportFlagBitsEXT::eInformation | vk::DebugReportFlagBitsEXT::eWarning | vk::DebugReportFlagBitsEXT::ePerformanceWarning | vk::DebugReportFlagBitsEXT::eError | vk::DebugReportFlagBitsEXT::eDebug)
+            .setPfnCallback(&debugReport);
+        const auto instanceInfo = vk::InstanceCreateInfo()
+            .setPNext(&debugReportCallbackCreateInfo)
+            .setPApplicationInfo(&app)
+            .setEnabledLayerCount(1)
+            .setPpEnabledLayerNames(instanceValidationLayers)
+            .setEnabledExtensionCount(3)
+            .setPpEnabledExtensionNames(instanceExtensionNames);
+        instance = vk::createInstanceUnique(instanceInfo);
+
+        debugReportCallback = UniqueDebugReportCallbackEXT(*instance, debugReportCallbackCreateInfo);
+
+        auto devices = instance->enumeratePhysicalDevices();
+        gpu = devices[0];
+
+        auto const surfaceCreateInfo = vk::Win32SurfaceCreateInfoKHR().setHinstance(hInstance).setHwnd(hWnd);
+        surface = instance->createWin32SurfaceKHRUnique(surfaceCreateInfo);
+
+        auto surfaceCapabilities = gpu.getSurfaceCapabilitiesKHR(*surface);
+
+        auto queueFamilyProperties = gpu.getQueueFamilyProperties();
+
+        bool foundQueueIndex = false;
+        uint32_t queueFamilyIndex;
+        for (uint32_t i = 0; i < queueFamilyProperties.size(); ++i) {
+            bool supportsPresent = gpu.getSurfaceSupportKHR(i, *surface);
+            auto& queueFamilyProperty = queueFamilyProperties[i];
+            if (supportsPresent && (queueFamilyProperty.queueFlags & vk::QueueFlagBits::eGraphics) && (queueFamilyProperty.queueFlags & vk::QueueFlagBits::eCompute) && (queueFamilyProperty.queueFlags & vk::QueueFlagBits::eTransfer)) {
+                queueFamilyIndex = i;
+                foundQueueIndex = true;
+                break;
+            }
+        }
+        assert(foundQueueIndex);
+
+        float priority = 0.0;
+        vk::DeviceQueueCreateInfo queueCreateInfo;
+        queueCreateInfo.setQueueFamilyIndex(queueFamilyIndex);
+        queueCreateInfo.setQueueCount(1);
+        queueCreateInfo.setPQueuePriorities(&priority);
+        char const *deviceExtensionNames[1] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
+        auto deviceCreateInfo = vk::DeviceCreateInfo()
+            .setQueueCreateInfoCount(1)
+            .setPQueueCreateInfos(&queueCreateInfo)
+            .setEnabledLayerCount(0)
+            .setPpEnabledLayerNames(nullptr)
+            .setEnabledExtensionCount(1)
+            .setPpEnabledExtensionNames(deviceExtensionNames)
+            .setPEnabledFeatures(nullptr);
+        device = gpu.createDeviceUnique(deviceCreateInfo);
+
+        const auto commandPoolCreateInfo = vk::CommandPoolCreateInfo().setQueueFamilyIndex(queueFamilyIndex);
+        commandPool = device->createCommandPoolUnique(commandPoolCreateInfo);
+
+        memoryProperties = gpu.getMemoryProperties();
+
+        const auto swapchainCreateInfo = vk::SwapchainCreateInfoKHR()
+            .setSurface(*surface)
+            .setMinImageCount(3)
+            .setImageFormat(framebufferColorPixelFormat)
+            .setImageColorSpace(vk::ColorSpaceKHR::eSrgbNonlinear)
+            .setImageExtent({ surfaceCapabilities.currentExtent.width, surfaceCapabilities.currentExtent.height })
+            .setImageArrayLayers(1)
+            .setImageUsage(vk::ImageUsageFlagBits::eColorAttachment)
+            .setImageSharingMode(vk::SharingMode::eExclusive)
+            .setQueueFamilyIndexCount(0)
+            .setPQueueFamilyIndices(nullptr)
+            .setPreTransform(vk::SurfaceTransformFlagBitsKHR::eIdentity)
+            .setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
+            .setPresentMode(vk::PresentModeKHR::eFifo)
+            .setClipped(true);
+        swapchain = device->createSwapchainKHRUnique(swapchainCreateInfo);
+
+        swapchainImages = device->getSwapchainImagesKHR(*swapchain);
+        for (auto& swapchainImage : swapchainImages) {
+            auto imageViewCreateInfo = vk::ImageViewCreateInfo()
+                .setViewType(vk::ImageViewType::e2D)
+                .setFormat(framebufferColorPixelFormat)
+                .setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1))
+                .setImage(swapchainImage);
+            swapchainImageViews.push_back(device->createImageViewUnique(imageViewCreateInfo));
+        }
+
+        queue = std::unique_ptr<QueueImpl>(new QueueImpl(*device, *commandPool, swapchainImageViews, *swapchain, surfaceCapabilities.currentExtent, framebufferColorPixelFormat, device->getQueue(queueFamilyIndex, 0)));
+
+        vk::PipelineCacheCreateInfo pipelineCacheCreateInfo;
+        pipelineCache = device->createPipelineCacheUnique(pipelineCacheCreateInfo);
+    }
+
+    Queue& DeviceImpl::getCommandQueue() {
+        return *queue;
+    }
+
+    vk::UniqueShaderModule DeviceImpl::prepareShader(const std::vector<uint8_t>& shader) {
+        auto const moduleCreateInfo = vk::ShaderModuleCreateInfo().setCodeSize(shader.size()).setPCode(reinterpret_cast<const uint32_t*>(shader.data()));
+        return device->createShaderModuleUnique(moduleCreateInfo);
+    }
+
+    std::pair<vk::UniquePipelineLayout, std::vector<vk::UniqueDescriptorSetLayout>> DeviceImpl::createPipelineLayout(const std::vector<std::vector<ResourceType>>& resources) {
+        std::vector<vk::UniqueDescriptorSetLayout> descriptorSetLayouts;
+        for (auto& resourceSet : resources) {
+            std::vector<vk::DescriptorSetLayoutBinding> bindings;
+            for (std::size_t i = 0; i < resourceSet.size(); ++i) {
+                vk::DescriptorType descriptorType;
+                switch (resourceSet[i]) {
+                case ResourceType::Texture:
+                    descriptorType = vk::DescriptorType::eSampledImage;
+                    break;
+                case ResourceType::Sampler:
+                    descriptorType = vk::DescriptorType::eSampler;
+                    break;
+                case ResourceType::UniformBufferObject:
+                    descriptorType = vk::DescriptorType::eUniformBuffer;
+                    break;
+                case ResourceType::ShaderStorageBufferObject:
+                    descriptorType = vk::DescriptorType::eStorageBuffer;
+                    break;
+                }
+                // Keep this in sync with RenderPassImpl::setResources().
+                bindings.emplace_back(vk::DescriptorSetLayoutBinding().setBinding(static_cast<uint32_t>(i)).setDescriptorType(descriptorType).setDescriptorCount(1).setStageFlags(vk::ShaderStageFlagBits::eAll));
+            }
+            const auto descriptorSetLayoutCreateInfo = vk::DescriptorSetLayoutCreateInfo().setBindingCount(static_cast<uint32_t>(resourceSet.size())).setPBindings(bindings.data());
+            descriptorSetLayouts.emplace_back(device->createDescriptorSetLayoutUnique(descriptorSetLayoutCreateInfo));
+        }
+        std::vector<vk::DescriptorSetLayout> weakDescriptorSetLayouts;
+        for (auto& descriptorSetLayout : descriptorSetLayouts)
+            weakDescriptorSetLayouts.push_back(*descriptorSetLayout);
+        const auto pipelineLayoutCreateInfo = vk::PipelineLayoutCreateInfo().setSetLayoutCount(static_cast<uint32_t>(weakDescriptorSetLayouts.size())).setPSetLayouts(weakDescriptorSetLayouts.data());
+        auto pipelineLayout = device->createPipelineLayoutUnique(pipelineLayoutCreateInfo);
+        return std::make_pair(std::move(pipelineLayout), std::move(descriptorSetLayouts));
+    }
+
+    vk::UniqueRenderPass DeviceImpl::createCompatibleRenderPass(const std::vector<PixelFormat>* colorPixelFormats) {
+        std::vector<vk::AttachmentDescription> attachmentDescriptions;
+        std::vector<vk::AttachmentReference> attachmentReferences;
+
+        if (colorPixelFormats == nullptr) {
+            attachmentDescriptions.emplace_back(vk::AttachmentDescription().setFormat(framebufferColorPixelFormat).setSamples(vk::SampleCountFlagBits::e1));
+            attachmentReferences.emplace_back(vk::AttachmentReference().setAttachment(0));
+        }
+        else {
+            for (std::size_t i = 0; i < colorPixelFormats->size(); ++i) {
+                vk::Format format = convertPixelFormat((*colorPixelFormats)[i]);
+                attachmentDescriptions.emplace_back(vk::AttachmentDescription().setFormat(format).setSamples(vk::SampleCountFlagBits::e1));
+                attachmentReferences.emplace_back(vk::AttachmentReference().setAttachment(static_cast<uint32_t>(i)));
+            }
+        }
+
+        const auto subpassDescription = vk::SubpassDescription()
+            .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics)
+            .setInputAttachmentCount(0)
+            .setPInputAttachments(nullptr)
+            .setColorAttachmentCount(static_cast<uint32_t>(attachmentReferences.size()))
+            .setPColorAttachments(attachmentReferences.data())
+            .setPResolveAttachments(nullptr)
+            .setPDepthStencilAttachment(nullptr)
+            .setPreserveAttachmentCount(0)
+            .setPPreserveAttachments(nullptr);
+
+        const auto renderPassCreateInfo = vk::RenderPassCreateInfo()
+            .setAttachmentCount(static_cast<uint32_t>(attachmentDescriptions.size()))
+            .setPAttachments(attachmentDescriptions.data())
+            .setSubpassCount(1)
+            .setPSubpasses(&subpassDescription)
+            .setDependencyCount(0)
+            .setPDependencies(nullptr);
+        return device->createRenderPassUnique(renderPassCreateInfo);
+    }
+
+    static vk::Format convertVertexFormat(RenderState::VertexFormat vertexFormat) {
+        switch (vertexFormat) {
+        case RenderState::VertexFormat::Float:
+            return vk::Format::eR32Sfloat;
+        case RenderState::VertexFormat::Float2:
+            return vk::Format::eR32G32Sfloat;
+        case RenderState::VertexFormat::Float3:
+            return vk::Format::eR32G32B32Sfloat;
+        case RenderState::VertexFormat::Float4:
+            return vk::Format::eR32G32B32A32Sfloat;
+        default:
+            assert(false);
+            return vk::Format::eR32Sfloat;
+        }
+    }
+
+    RenderState& DeviceImpl::getRenderState(const std::vector<uint8_t>& vertexShader, const std::string& vertexShaderName, const std::vector<uint8_t>& fragmentShader, const std::string& fragmentShaderName, const std::vector<unsigned int>& vertexAttributeBufferStrides, const std::vector<RenderState::VertexAttribute>& vertexAttributes, const std::vector<std::vector<ResourceType>>& resources, const std::vector<PixelFormat>* colorPixelFormats) {
+        auto vertexShaderModule = prepareShader(vertexShader);
+        auto fragmentShaderModule = prepareShader(fragmentShader);
+        const vk::PipelineShaderStageCreateInfo shaderStages[2] = {
+            vk::PipelineShaderStageCreateInfo().setStage(vk::ShaderStageFlagBits::eVertex).setModule(*vertexShaderModule).setPName(vertexShaderName.c_str()),
+            vk::PipelineShaderStageCreateInfo().setStage(vk::ShaderStageFlagBits::eFragment).setModule(*fragmentShaderModule).setPName(fragmentShaderName.c_str()) };
+
+        std::vector<vk::VertexInputBindingDescription> bindingDescriptions;
+        for (std::size_t i = 0; i < vertexAttributeBufferStrides.size(); ++i) {
+            unsigned int vertexAttributeBufferStride = vertexAttributeBufferStrides[i];
+            bindingDescriptions.emplace_back(vk::VertexInputBindingDescription().setBinding(static_cast<uint32_t>(i)).setStride(vertexAttributeBufferStride).setInputRate(vk::VertexInputRate::eVertex));
+        }
+
+        std::vector<vk::VertexInputAttributeDescription> attributeDescriptions;
+        for (std::size_t i = 0; i < vertexAttributes.size(); ++i) {
+            const auto& vertexAttribute = vertexAttributes[i];
+            attributeDescriptions.push_back(vk::VertexInputAttributeDescription().setLocation(static_cast<uint32_t>(i)).setBinding(vertexAttribute.vertexAttributeBufferIndex).setFormat(convertVertexFormat(vertexAttribute.format)).setOffset(vertexAttribute.offsetWithinStride));
+        }
+
+        const auto vertexInputInfo = vk::PipelineVertexInputStateCreateInfo()
+            .setVertexBindingDescriptionCount(static_cast<uint32_t>(vertexAttributeBufferStrides.size()))
+            .setPVertexBindingDescriptions(bindingDescriptions.data())
+            .setVertexAttributeDescriptionCount(static_cast<uint32_t>(vertexAttributes.size()))
+            .setPVertexAttributeDescriptions(attributeDescriptions.data());
+
+        const auto inputAssemblyInfo = vk::PipelineInputAssemblyStateCreateInfo().setTopology(vk::PrimitiveTopology::eTriangleList);
+
+        const auto viewportInfo = vk::PipelineViewportStateCreateInfo().setViewportCount(1).setScissorCount(1);
+
+        const auto rasterizationInfo = vk::PipelineRasterizationStateCreateInfo()
+            .setDepthClampEnable(VK_FALSE)
+            .setRasterizerDiscardEnable(VK_FALSE)
+            .setPolygonMode(vk::PolygonMode::eFill)
+            .setCullMode(vk::CullModeFlagBits::eBack)
+            .setFrontFace(vk::FrontFace::eClockwise)
+            .setDepthBiasEnable(VK_FALSE)
+            .setLineWidth(1.0f);
+
+        const auto multisampleInfo = vk::PipelineMultisampleStateCreateInfo();
+
+        const auto stencilOp = vk::StencilOpState()
+            .setFailOp(vk::StencilOp::eKeep)
+            .setPassOp(vk::StencilOp::eKeep)
+            .setCompareOp(vk::CompareOp::eAlways);
+
+        const auto depthStencilInfo = vk::PipelineDepthStencilStateCreateInfo()
+            .setDepthTestEnable(VK_FALSE)
+            .setDepthWriteEnable(VK_FALSE)
+            .setDepthCompareOp(vk::CompareOp::eNever)
+            .setDepthBoundsTestEnable(VK_FALSE)
+            .setStencilTestEnable(VK_FALSE)
+            .setFront(stencilOp)
+            .setBack(stencilOp);
+
+        const vk::PipelineColorBlendAttachmentState colorBlendAttachments[1] = {
+            vk::PipelineColorBlendAttachmentState().setColorWriteMask(
+                vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB |
+                vk::ColorComponentFlagBits::eA) };
+
+        const auto colorBlendInfo =
+            vk::PipelineColorBlendStateCreateInfo().setAttachmentCount(1).setPAttachments(colorBlendAttachments);
+
+        const vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
+
+        const auto dynamicStateInfo = vk::PipelineDynamicStateCreateInfo().setPDynamicStates(dynamicStates).setDynamicStateCount(2);
+
+        auto pipelineLayout = createPipelineLayout(resources);
+        auto compatibleRenderPass = createCompatibleRenderPass(colorPixelFormats);
+
+        const auto graphicsPipelineCreateInfo = vk::GraphicsPipelineCreateInfo()
+            .setStageCount(2)
+            .setPStages(shaderStages)
+            .setPVertexInputState(&vertexInputInfo)
+            .setPInputAssemblyState(&inputAssemblyInfo)
+            .setPViewportState(&viewportInfo)
+            .setPRasterizationState(&rasterizationInfo)
+            .setPMultisampleState(&multisampleInfo)
+            .setPDepthStencilState(&depthStencilInfo)
+            .setPColorBlendState(&colorBlendInfo)
+            .setPDynamicState(&dynamicStateInfo)
+            .setLayout(*pipelineLayout.first)
+            .setRenderPass(*compatibleRenderPass);
+        auto graphicsPipeline = device->createGraphicsPipelineUnique(*pipelineCache, graphicsPipelineCreateInfo);
+        renderStates.emplace_back(RenderStateImpl(std::move(graphicsPipeline), std::move(pipelineLayout.second), std::move(pipelineLayout.first), std::move(compatibleRenderPass)));
+        return renderStates.back();
+    }
+
+    ComputeState& DeviceImpl::getComputeState(const std::vector<uint8_t>& shader, const std::string& shaderName, const std::vector<std::vector<ResourceType>>& resources) {
+        auto shaderModule = prepareShader(shader);
+        const auto shaderStage = vk::PipelineShaderStageCreateInfo().setStage(vk::ShaderStageFlagBits::eCompute).setModule(*shaderModule).setPName(shaderName.c_str());
+
+        auto pipelineLayout = createPipelineLayout(resources);
+
+        const auto computePipelineCreateInfo = vk::ComputePipelineCreateInfo()
+            .setStage(shaderStage)
+            .setLayout(*pipelineLayout.first);
+        auto computePipeline = device->createComputePipelineUnique(*pipelineCache, computePipelineCreateInfo);
+        computeStates.emplace_back(ComputeStateImpl(std::move(computePipeline), std::move(pipelineLayout.second), std::move(pipelineLayout.first)));
+        return computeStates.back();
+    }
+
+    BufferHolder DeviceImpl::getBuffer(unsigned int length) {
+        auto result = bufferCache.emplace(length, ResourcePool<BufferImpl>());
+        auto insertionTookPlace = result.second;
+        auto& resourcePool = result.first->second;
+        if (!resourcePool.freeList.empty()) {
+            // FIXME: Zero-fill the texture somehow.
+            auto buffer = std::move(resourcePool.freeList.back());
+            resourcePool.freeList.pop_back();
+            buffer.incrementReferenceCount();
+            resourcePool.inUse.emplace_back(std::move(buffer));
+            return BufferHolder(*this, resourcePool.inUse.back());
+        }
+
+        const auto bufferCreateInfo = vk::BufferCreateInfo().setSize(length).setUsage(vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eUniformBuffer | vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eIndirectBuffer);
+        auto buffer = device->createBufferUnique(bufferCreateInfo);
+        auto memoryRequirements = device->getBufferMemoryRequirements(*buffer);
+        auto memoryAllocateInfo = vk::MemoryAllocateInfo().setAllocationSize(memoryRequirements.size);
+        bool found = false;
+        for (int i = 0; i < VK_MAX_MEMORY_TYPES; ++i) {
+            if ((memoryRequirements.memoryTypeBits & (1 << i)) && (memoryProperties.memoryTypes[i].propertyFlags & (vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent))) {
+                memoryAllocateInfo.setMemoryTypeIndex(i);
+                found = true;
+                break;
+            }
+        }
+        assert(found);
+        auto memory = device->allocateMemoryUnique(memoryAllocateInfo);
+        device->bindBufferMemory(*buffer, *memory, 0);
+        // FIXME: Zero-fill the texture somehow.
+        std::function<void(BufferImpl&)> returnToDevice = [&](BufferImpl& buffer) {
+            auto iterator = bufferCache.find(buffer.getLength());
+            assert(iterator != bufferCache.end());
+            auto& resourcePool = iterator->second;
+            for (auto listIterator = resourcePool.inUse.begin(); listIterator != resourcePool.inUse.end(); ++listIterator) {
+                if (&(*listIterator) == &buffer) {
+                    auto buffer = std::move(*listIterator);
+                    resourcePool.inUse.erase(listIterator);
+                    resourcePool.freeList.emplace_back(std::move(buffer));
+                    return;
+                }
+            }
+            assert(false);
+        };
+        BufferImpl newBuffer(std::move(returnToDevice), length, *device, std::move(memory), std::move(buffer));
+        newBuffer.incrementReferenceCount();
+        resourcePool.inUse.emplace_back(std::move(newBuffer));
+        return BufferHolder(*this, resourcePool.inUse.back());
+    }
+
+    void DeviceImpl::returnBuffer(Buffer& buffer) {
+        auto* downcast = dynamic_cast<BufferImpl*>(&buffer);
+        assert(downcast != nullptr);
+        downcast->decrementReferenceCount();
+    }
+
+    TextureHolder DeviceImpl::getTexture(unsigned int width, unsigned int height, PixelFormat format) {
+        TextureParameters textureParameters = { width, height, format };
+        auto result = textureCache.emplace(std::move(textureParameters), ResourcePool<TextureImpl>());
+        auto insertionTookPlace = result.second;
+        auto& resourcePool = result.first->second;
+        if (!resourcePool.freeList.empty()) {
+            // FIXME: Zero-fill the texture somehow.
+            auto buffer = std::move(resourcePool.freeList.back());
+            resourcePool.freeList.pop_back();
+            resourcePool.inUse.emplace_back(std::move(buffer));
+            return TextureHolder(*this, resourcePool.inUse.back());
+        }
+
+        auto vulkanPixelFormat = convertPixelFormat(format);
+        auto formatProperties = gpu.getFormatProperties(vulkanPixelFormat);
+        assert(formatProperties.linearTilingFeatures & vk::FormatFeatureFlagBits::eSampledImage);
+
+        // FIXME: Use staging textures so we can write into the texture linearly.
+        const auto imageCreateInfo = vk::ImageCreateInfo()
+            .setImageType(vk::ImageType::e2D)
+            .setFormat(vulkanPixelFormat)
+            .setExtent({ width, height, 1 })
+            .setMipLevels(1)
+            .setArrayLayers(1)
+            .setSamples(vk::SampleCountFlagBits::e1)
+            .setTiling(vk::ImageTiling::eOptimal)
+            .setUsage(vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eColorAttachment)
+            .setSharingMode(vk::SharingMode::eExclusive)
+            .setQueueFamilyIndexCount(0)
+            .setPQueueFamilyIndices(nullptr)
+            .setInitialLayout(vk::ImageLayout::ePreinitialized);
+        auto image = device->createImageUnique(imageCreateInfo);
+        auto memoryRequirements = device->getImageMemoryRequirements(*image);
+        auto memoryAllocateInfo = vk::MemoryAllocateInfo().setAllocationSize(memoryRequirements.size);
+        bool found = false;
+        for (int i = 0; i < VK_MAX_MEMORY_TYPES; ++i) {
+            if ((memoryRequirements.memoryTypeBits & (1 << i))) {
+                memoryAllocateInfo.setMemoryTypeIndex(i);
+                found = true;
+                break;
+            }
+        }
+        assert(found);
+        auto memory = device->allocateMemoryUnique(memoryAllocateInfo);
+        device->bindImageMemory(*image, *memory, 0);
+        // FIXME: Zero-fill the texture somehow.
+        const auto imageViewCreateInfo = vk::ImageViewCreateInfo()
+            .setImage(*image)
+            .setViewType(vk::ImageViewType::e2D)
+            .setFormat(vulkanPixelFormat)
+            .setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));;
+        auto imageView = device->createImageViewUnique(imageViewCreateInfo);
+        std::function<void(TextureImpl&)> returnToDevice = [&](TextureImpl& texture) {
+            TextureParameters textureParameters = { texture.getWidth(), texture.getHeight(), convertFormat(texture.getFormat()) };
+            auto iterator = textureCache.find(textureParameters);
+            assert(iterator != textureCache.end());
+            auto& resourcePool = iterator->second;
+            for (auto listIterator = resourcePool.inUse.begin(); listIterator != resourcePool.inUse.end(); ++listIterator) {
+                if (&(*listIterator) == &texture) {
+                    auto buffer = std::move(*listIterator);
+                    resourcePool.inUse.erase(listIterator);
+                    resourcePool.freeList.emplace_back(std::move(buffer));
+                    return;
+                }
+            }
+            assert(false);
+        };
+        TextureImpl newTexture(std::move(returnToDevice), width, height, vulkanPixelFormat, *device, std::move(memory), std::move(image), std::move(imageView));
+        newTexture.incrementReferenceCount();
+        resourcePool.inUse.emplace_back(std::move(newTexture));
+        return TextureHolder(*this, resourcePool.inUse.back());
+    }
+
+    void DeviceImpl::returnTexture(Texture& texture) {
+        auto* downcast = dynamic_cast<TextureImpl*>(&texture);
+        assert(downcast != nullptr);
+        downcast->decrementReferenceCount();
+    }
+
+    SamplerHolder DeviceImpl::getSampler(AddressMode addressMode, Filter filter) {
+        auto result = samplerCache.emplace((static_cast<uint32_t>(addressMode) << 8) | static_cast<uint32_t>(filter), ResourcePool<SamplerImpl>());
+        auto insertionTookPlace = result.second;
+        auto& resourcePool = result.first->second;
+        if (!resourcePool.freeList.empty()) {
+            auto sampler = std::move(resourcePool.freeList.back());
+            resourcePool.freeList.pop_back();
+            sampler.incrementReferenceCount();
+            resourcePool.inUse.emplace_back(std::move(sampler));
+            return SamplerHolder(*this, resourcePool.inUse.back());
+        }
+
+        vk::Filter vulkanFilter;
+        vk::SamplerMipmapMode mipmapMode;
+        switch (filter) {
+        case Filter::Nearest:
+            vulkanFilter = vk::Filter::eNearest;
+            mipmapMode = vk::SamplerMipmapMode::eNearest;
+            break;
+        case Filter::Linear:
+            vulkanFilter = vk::Filter::eLinear;
+            mipmapMode = vk::SamplerMipmapMode::eLinear;
+            break;
+        default:
+            assert(false);
+        }
+
+        vk::SamplerAddressMode vulkanAddressMode;
+        switch (addressMode) {
+        case AddressMode::ClampToEdge:
+            vulkanAddressMode = vk::SamplerAddressMode::eClampToEdge;
+            break;
+        case AddressMode::MirrorClampToEdge:
+            vulkanAddressMode = vk::SamplerAddressMode::eMirrorClampToEdge;
+            break;
+        case AddressMode::Repeat:
+            vulkanAddressMode = vk::SamplerAddressMode::eRepeat;
+            break;
+        case AddressMode::MirrorRepeat:
+            vulkanAddressMode = vk::SamplerAddressMode::eMirroredRepeat;
+            break;
+        }
+
+        const auto samplerCreateInfo = vk::SamplerCreateInfo()
+            .setMagFilter(vulkanFilter)
+            .setMinFilter(vulkanFilter)
+            .setMipmapMode(mipmapMode)
+            .setAddressModeU(vulkanAddressMode)
+            .setAddressModeV(vulkanAddressMode)
+            .setAddressModeW(vulkanAddressMode);
+        auto sampler = device->createSamplerUnique(samplerCreateInfo);
+
+        std::function<void(SamplerImpl&)> returnToDevice = [&](SamplerImpl& sampler) {
+            AddressMode addressMode;
+            switch (sampler.getAddressMode()) {
+            case vk::SamplerAddressMode::eClampToEdge:
+                addressMode = AddressMode::ClampToEdge;
+                break;
+            case vk::SamplerAddressMode::eMirrorClampToEdge:
+                addressMode = AddressMode::MirrorClampToEdge;
+                break;
+            case vk::SamplerAddressMode::eRepeat:
+                addressMode = AddressMode::Repeat;
+                break;
+            case vk::SamplerAddressMode::eMirroredRepeat:
+                addressMode = AddressMode::MirrorRepeat;
+                break;
+            default:
+                assert(false);
+            }
+            Filter filter;
+            switch (sampler.getFilter()) {
+            case vk::Filter::eNearest:
+                filter = Filter::Nearest;
+                break;
+            case vk::Filter::eLinear:
+                filter = Filter::Linear;
+                break;
+            default:
+                assert(false);
+            }
+
+            auto iterator = samplerCache.find((static_cast<uint32_t>(addressMode) << 8) | static_cast<uint32_t>(filter));
+            assert(iterator != samplerCache.end());
+            auto& resourcePool = iterator->second;
+            for (auto listIterator = resourcePool.inUse.begin(); listIterator != resourcePool.inUse.end(); ++listIterator) {
+                if (&(*listIterator) == &sampler) {
+                    auto sampler = std::move(*listIterator);
+                    resourcePool.inUse.erase(listIterator);
+                    resourcePool.freeList.emplace_back(std::move(sampler));
+                    return;
+                }
+            }
+            assert(false);
+        };
+        SamplerImpl newSampler(std::move(returnToDevice), vulkanFilter, mipmapMode, vulkanAddressMode, *device, std::move(sampler));
+        newSampler.incrementReferenceCount();
+        resourcePool.inUse.emplace_back(std::move(newSampler));
+        return SamplerHolder(*this, resourcePool.inUse.back());
+    }
+
+    void DeviceImpl::returnSampler(Sampler& sampler) {
+        auto* downcast = dynamic_cast<SamplerImpl*>(&sampler);
+        assert(downcast != nullptr);
+        downcast->decrementReferenceCount();
+    }
+
+    DeviceImpl::~DeviceImpl() {
+        for (std::size_t i = 0; i < swapchainImages.size(); ++i)
+            static_cast<Queue*>(queue.get())->present();
+    }
+}
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/DeviceImpl.h
new file mode 100644 (file)
index 0000000..a9128a9
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include <memory>
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <list>
+#include <unordered_map>
+#include <vulkan/vulkan.hpp>
+#include "QueueImpl.h"
+#include "BufferImpl.h"
+#include "TextureImpl.h"
+#include "SamplerImpl.h"
+#include "RenderStateImpl.h"
+#include "ComputeStateImpl.h"
+#include <cassert>
+
+namespace WebGPU {
+    class DeviceImpl : public Device {
+    public:
+        DeviceImpl() = default;
+        virtual ~DeviceImpl();
+        DeviceImpl(const DeviceImpl&) = delete;
+        DeviceImpl(DeviceImpl&&) = default;
+        DeviceImpl& operator=(const DeviceImpl&) = delete;
+        DeviceImpl& operator=(DeviceImpl&&) = default;
+
+        DeviceImpl(HINSTANCE hInstance, HWND hWnd);
+
+    private:
+        class UniqueDebugReportCallbackEXT {
+        public:
+            UniqueDebugReportCallbackEXT() = default;
+            UniqueDebugReportCallbackEXT(const UniqueDebugReportCallbackEXT&) = delete;
+            UniqueDebugReportCallbackEXT(UniqueDebugReportCallbackEXT&& other) : debugReportCallback(std::move(other.debugReportCallback)), instance(std::move(other.instance)) {
+                other.debugReportCallback = vk::DebugReportCallbackEXT();
+            }
+            UniqueDebugReportCallbackEXT& operator=(const UniqueDebugReportCallbackEXT&) = delete;
+            UniqueDebugReportCallbackEXT& operator=(UniqueDebugReportCallbackEXT&& other) {
+                destroy();
+                debugReportCallback = std::move(other.debugReportCallback);
+                instance = std::move(other.instance);
+                other.debugReportCallback = vk::DebugReportCallbackEXT();
+                return *this;
+            }
+
+            UniqueDebugReportCallbackEXT(const vk::Instance& instance, const vk::DebugReportCallbackCreateInfoEXT& info) : instance(instance) {
+                PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>(instance.getProcAddr("vkCreateDebugReportCallbackEXT"));
+                assert(createDebugReportCallback != nullptr);
+                const VkDebugReportCallbackCreateInfoEXT infoValue = info;
+                VkDebugReportCallbackEXT debugReportCallback;
+                auto result = createDebugReportCallback(instance, &infoValue, nullptr, &debugReportCallback);
+                assert(result == VK_SUCCESS);
+                this->debugReportCallback = debugReportCallback;
+            }
+
+            ~UniqueDebugReportCallbackEXT() {
+                destroy();
+            }
+
+            void destroy() {
+                if (debugReportCallback != vk::DebugReportCallbackEXT()) {
+                    PFN_vkDestroyDebugReportCallbackEXT destroyDebugReportCallback = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>(instance.getProcAddr("vkDestroyDebugReportCallbackEXT"));
+                    assert(destroyDebugReportCallback != nullptr);
+                    destroyDebugReportCallback(instance, debugReportCallback, nullptr);
+                }
+            }
+            vk::DebugReportCallbackEXT debugReportCallback;
+            vk::Instance instance;
+        };
+
+        Queue& getCommandQueue() override;
+        RenderState& getRenderState(const std::vector<uint8_t>& vertexShader, const std::string& vertexShaderName, const std::vector<uint8_t>& fragmentShader, const std::string& fragmentShaderName, const std::vector<unsigned int>& vertexAttributeBufferStrides, const std::vector<RenderState::VertexAttribute>& vertexAttributes, const std::vector<std::vector<ResourceType>>& resources, const std::vector<PixelFormat>* colorPixelFormats) override;
+        ComputeState& getComputeState(const std::vector<uint8_t>& shader, const std::string& shaderName, const std::vector<std::vector<ResourceType>>& resources) override;
+        BufferHolder getBuffer(unsigned int length) override;
+        void returnBuffer(Buffer&) override;
+        TextureHolder getTexture(unsigned int width, unsigned int height, PixelFormat format) override;
+        void returnTexture(Texture&) override;
+        SamplerHolder getSampler(AddressMode addressMode, Filter filter) override;
+        void returnSampler(Sampler&) override;
+
+        vk::UniqueShaderModule prepareShader(const std::vector<uint8_t>& shader);
+        std::pair<vk::UniquePipelineLayout, std::vector<vk::UniqueDescriptorSetLayout>> createPipelineLayout(const std::vector<std::vector<ResourceType>>& resources);
+        vk::UniqueRenderPass createCompatibleRenderPass(const std::vector<PixelFormat>* colorPixelFormats);
+
+        vk::UniqueInstance instance;
+        UniqueDebugReportCallbackEXT debugReportCallback;
+        vk::PhysicalDevice gpu;
+        vk::UniqueSurfaceKHR surface;
+        vk::UniqueDevice device;
+        vk::PhysicalDeviceMemoryProperties memoryProperties;
+        vk::UniqueSwapchainKHR swapchain;
+        std::vector<vk::Image> swapchainImages;
+        std::vector<vk::UniqueImageView> swapchainImageViews;
+        vk::UniquePipelineCache pipelineCache;
+        std::unique_ptr<QueueImpl> queue;
+        vk::UniqueCommandPool commandPool;
+        std::list<RenderStateImpl> renderStates;
+        std::list<ComputeStateImpl> computeStates;
+
+        template<typename T>
+        struct ResourcePool {
+            std::list<T> inUse;
+            std::list<T> freeList;
+        };
+
+        std::unordered_map<unsigned int, ResourcePool<BufferImpl>> bufferCache;
+
+        struct TextureParameters {
+            bool operator==(const TextureParameters& other) const {
+                return width == other.width && height == other.height && format == other.format;
+            }
+
+            unsigned int width;
+            unsigned int height;
+            PixelFormat format;
+        };
+        struct TextureParametersHash {
+            std::size_t operator()(const TextureParameters& textureParameters) const {
+                std::hash<unsigned int> uintHash;
+                return uintHash(textureParameters.width) | uintHash(textureParameters.height) | uintHash(static_cast<unsigned int>(textureParameters.format));
+            }
+        };
+        std::unordered_map<TextureParameters, ResourcePool<TextureImpl>, TextureParametersHash> textureCache;
+
+        std::unordered_map<uint32_t, ResourcePool<SamplerImpl>> samplerCache;
+    };
+}
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.cpp
new file mode 100644 (file)
index 0000000..1263972
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "HostAccessPassImpl.h"
+#include <cassert>
+
+namespace WebGPU {
+    HostAccessPassImpl::HostAccessPassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer) : PassImpl(device, std::move(commandBuffer)), finishedEvent(device.createEventUnique(vk::EventCreateInfo())) {
+        // Our regular inter-command-buffer synchronization also issues barriers with eHostRead and eHostWrite, so
+        // the host just needs to wait for any previously-committed command buffers to finish (which it already has
+        // to do because we can only have a few framebuffer images we can draw into). Therefore, we only append a
+        // single command into the command buffer: waitEvents(), which the CPU will signal when it's done with its
+        // work. Therefore, the host has ownership of the resources while the GPU is waiting. As soon as its done
+        // waiting, our regular inter-command-buffer synchronization will issue the relevant barriers with eHostRead
+        // and eHostWrite.
+    }
+
+    void HostAccessPassImpl::overwriteBuffer(Buffer& buffer, const std::vector<uint8_t>& newData) {
+        auto* downcast = dynamic_cast<BufferImpl*>(&buffer);
+        assert(downcast != nullptr);
+        auto& buf = *downcast;
+
+        assert(buf.getLength() == newData.size());
+
+        buffersToOverwrite.push_back(buf);
+        bufferOverwriteData.push_back(newData);
+
+        insertBuffer(buf);
+    }
+
+    boost::unique_future<std::vector<uint8_t>> HostAccessPassImpl::getBufferContents(Buffer& buffer) {
+        auto* downcast = dynamic_cast<BufferImpl*>(&buffer);
+        assert(downcast != nullptr);
+        auto& buf = *downcast;
+
+        buffersToRead.push_back(buf);
+
+        insertBuffer(buf);
+
+        readPromises.emplace_back(boost::promise<std::vector<uint8_t>>());
+        return readPromises.back().get_future();
+    }
+
+    void HostAccessPassImpl::execute() {
+        assert(buffersToOverwrite.size() == bufferOverwriteData.size());
+        for (std::size_t i = 0; i < buffersToOverwrite.size(); ++i) {
+            BufferImpl& buffer = buffersToOverwrite[i];
+            auto& data = bufferOverwriteData[i];
+            void* cpuAddress = device.mapMemory(buffer.getDeviceMemory(), 0, VK_WHOLE_SIZE);
+            memcpy(cpuAddress, data.data(), buffer.getLength());
+            device.unmapMemory(buffer.getDeviceMemory());
+        }
+
+        assert(buffersToRead.size() == readPromises.size());
+        for (std::size_t i = 0; i < buffersToRead.size(); ++i) {
+            BufferImpl& buffer = buffersToRead[i];
+            std::vector<uint8_t> data(buffer.getLength());
+            void* cpuAddress = device.mapMemory(buffer.getDeviceMemory(), 0, VK_WHOLE_SIZE);
+            memcpy(data.data(), cpuAddress, buffer.getLength());
+            device.unmapMemory(buffer.getDeviceMemory());
+            readPromises[i].set_value(data);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/HostAccessPassImpl.h
new file mode 100644 (file)
index 0000000..292ef57
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include "PassImpl.h"
+#include "BufferImpl.h"
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class HostAccessPassImpl : public PassImpl, public HostAccessPass {
+    public:
+        HostAccessPassImpl() = default;
+        virtual ~HostAccessPassImpl() = default;
+        HostAccessPassImpl(const HostAccessPassImpl&) = delete;
+        HostAccessPassImpl(HostAccessPassImpl&&) = default;
+        HostAccessPassImpl& operator=(const HostAccessPassImpl&) = delete;
+        HostAccessPassImpl& operator=(HostAccessPassImpl&&) = default;
+
+        HostAccessPassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer);
+
+        vk::Event getFinishedEvent() const { return *finishedEvent; }
+
+        void execute();
+
+    private:
+        void overwriteBuffer(Buffer& buffer, const std::vector<uint8_t>& newData) override;
+        boost::unique_future<std::vector<uint8_t>> getBufferContents(Buffer& buffer) override;
+
+        vk::UniqueEvent finishedEvent;
+        std::vector<std::reference_wrapper<BufferImpl>> buffersToOverwrite;
+        std::vector<std::vector<uint8_t>> bufferOverwriteData;
+        std::vector<std::reference_wrapper<BufferImpl>> buffersToRead;
+        std::vector<boost::promise<std::vector<uint8_t>>> readPromises;
+    };
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.cpp
new file mode 100644 (file)
index 0000000..8345abd
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "PassImpl.h"
+
+namespace WebGPU {
+    PassImpl::PassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer) : device(device), commandBuffer(std::move(commandBuffer)) {
+    }
+
+    // Keep this in sync with DeviceImpl::createPipelineLayout().
+    class ResourceVisitor : public boost::static_visitor<void> {
+    public:
+        void operator()(const TextureReference& texture) {
+            auto* downcast = dynamic_cast<TextureImpl*>(&texture.get());
+            assert(downcast != nullptr);
+            auto& tex = *downcast;
+            bindings.emplace_back(vk::DescriptorSetLayoutBinding().setBinding(count).setDescriptorType(vk::DescriptorType::eSampledImage).setDescriptorCount(1).setStageFlags(vk::ShaderStageFlagBits::eAll));
+            descriptorImageInfos.emplace_back(vk::DescriptorImageInfo().setImageView(tex.getImageView()).setImageLayout(vk::ImageLayout::eGeneral));
+            writeDescriptorSets.emplace_back(vk::WriteDescriptorSet().setDstBinding(count).setDstArrayElement(0).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eSampledImage));
+            textures.push_back(tex);
+            ++count;
+            ++imageCount;
+        }
+
+        void operator()(const SamplerReference& samplerObject) {
+            auto* downcast = dynamic_cast<SamplerImpl*>(&samplerObject.get());
+            assert(downcast != nullptr);
+            auto& sampler = *downcast;
+            bindings.emplace_back(vk::DescriptorSetLayoutBinding().setBinding(count).setDescriptorType(vk::DescriptorType::eSampler).setDescriptorCount(1).setStageFlags(vk::ShaderStageFlagBits::eAll));
+            descriptorImageInfos.emplace_back(vk::DescriptorImageInfo().setSampler(sampler.getSampler()));
+            writeDescriptorSets.emplace_back(vk::WriteDescriptorSet().setDstBinding(count).setDstArrayElement(0).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eSampler));
+            samplers.push_back(sampler);
+            ++count;
+            ++samplerCount;
+        }
+
+        void operator()(const UniformBufferObjectReference& uniformBufferObject) {
+            auto* downcast = dynamic_cast<BufferImpl*>(&uniformBufferObject.get());
+            assert(downcast != nullptr);
+            auto& ubo = *downcast;
+            bindings.emplace_back(vk::DescriptorSetLayoutBinding().setBinding(count).setDescriptorType(vk::DescriptorType::eUniformBuffer).setDescriptorCount(1).setStageFlags(vk::ShaderStageFlagBits::eAll));
+            descriptorBufferInfos.emplace_back(vk::DescriptorBufferInfo().setBuffer(ubo.getBuffer()).setRange(VK_WHOLE_SIZE));
+            writeDescriptorSets.emplace_back(vk::WriteDescriptorSet().setDstBinding(count).setDstArrayElement(0).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eUniformBuffer));
+            buffers.push_back(ubo);
+            ++count;
+            ++uniformBufferCount;
+        }
+
+        void operator()(const ShaderStorageBufferObjectReference& shaderStorageBufferObject) {
+            auto* downcast = dynamic_cast<BufferImpl*>(&shaderStorageBufferObject.get());
+            assert(downcast != nullptr);
+            auto& ssbo = *downcast;
+            bindings.emplace_back(vk::DescriptorSetLayoutBinding().setBinding(count).setDescriptorType(vk::DescriptorType::eStorageBuffer).setDescriptorCount(1).setStageFlags(vk::ShaderStageFlagBits::eAll));
+            descriptorBufferInfos.emplace_back(vk::DescriptorBufferInfo().setBuffer(ssbo.getBuffer()).setOffset(0).setRange(VK_WHOLE_SIZE));
+            writeDescriptorSets.emplace_back(vk::WriteDescriptorSet().setDstBinding(count).setDstArrayElement(0).setDescriptorCount(1).setDescriptorType(vk::DescriptorType::eStorageBuffer));
+            buffers.push_back(ssbo);
+            ++count;
+            ++storageBufferCount;
+        }
+
+        const std::vector<vk::DescriptorSetLayoutBinding>& getBindings() const { return bindings; }
+        std::vector<vk::WriteDescriptorSet>&& releaseWriteDescriptorSets() { return std::move(writeDescriptorSets); }
+        const std::vector<vk::DescriptorImageInfo>& getDescriptorImageInfos() const { return descriptorImageInfos; }
+        const std::vector<vk::DescriptorBufferInfo>& getDescriptorBufferInfos() const { return descriptorBufferInfos; }
+        const std::vector<std::reference_wrapper<BufferImpl>>& getBuffers() const { return buffers; }
+        const std::vector<std::reference_wrapper<TextureImpl>>& getTextures() const { return textures; }
+        const std::vector<std::reference_wrapper<SamplerImpl>>& getSamplers() const { return samplers; }
+        uint32_t getImageCount() const { return imageCount; }
+        uint32_t getSamplerCount() const { return samplerCount; }
+        uint32_t getUniformBufferCount() const { return uniformBufferCount; }
+        uint32_t getStorageBufferCount() const { return storageBufferCount; }
+
+    private:
+        std::vector<vk::DescriptorSetLayoutBinding> bindings;
+        std::vector<vk::WriteDescriptorSet> writeDescriptorSets;
+        std::vector<vk::DescriptorImageInfo> descriptorImageInfos;
+        std::vector<vk::DescriptorBufferInfo> descriptorBufferInfos;
+        std::vector<std::reference_wrapper<BufferImpl>> buffers;
+        std::vector<std::reference_wrapper<TextureImpl>> textures;
+        std::vector<std::reference_wrapper<SamplerImpl>> samplers;
+        uint32_t count{ 0 };
+        uint32_t imageCount{ 0 };
+        uint32_t samplerCount{ 0 };
+        uint32_t uniformBufferCount{ 0 };
+        uint32_t storageBufferCount{ 0 };
+    };
+
+    void PassImpl::setResources(vk::PipelineBindPoint pipelineBindPoint, unsigned int index, const std::vector<WebGPU::ResourceReference>& resourceReferences) {
+        ResourceVisitor resourceVisitor;
+        for (const auto& resourceReference : resourceReferences)
+            boost::apply_visitor(resourceVisitor, resourceReference);
+        vk::DescriptorPoolSize const poolSizes[] = {
+            vk::DescriptorPoolSize().setType(vk::DescriptorType::eSampledImage).setDescriptorCount(resourceVisitor.getImageCount()),
+            vk::DescriptorPoolSize().setType(vk::DescriptorType::eSampler).setDescriptorCount(resourceVisitor.getSamplerCount()),
+            vk::DescriptorPoolSize().setType(vk::DescriptorType::eUniformBuffer).setDescriptorCount(resourceVisitor.getUniformBufferCount()),
+            vk::DescriptorPoolSize().setType(vk::DescriptorType::eStorageBuffer).setDescriptorCount(resourceVisitor.getStorageBufferCount()) };
+        const auto descriptorPoolCreateInfo = vk::DescriptorPoolCreateInfo().setMaxSets(1).setPoolSizeCount(sizeof(poolSizes) / sizeof(poolSizes[0])).setPPoolSizes(poolSizes);
+        descriptorPools.emplace_back(device.createDescriptorPoolUnique(descriptorPoolCreateInfo));
+        auto descriptorPool = *descriptorPools.back();
+
+        const auto& bindings = resourceVisitor.getBindings();
+        const auto descriptorSetLayoutCreateInfo = vk::DescriptorSetLayoutCreateInfo().setBindingCount(static_cast<uint32_t>(bindings.size())).setPBindings(bindings.data());
+        descriptorSetLayouts.emplace_back(device.createDescriptorSetLayoutUnique(descriptorSetLayoutCreateInfo));
+        auto descriptorSetLayout = *descriptorSetLayouts.back();
+        // FIXME: Does the argument to setPSetLayouts below need to be pointer-equal to one of the descriptor sets used to create the Vulkan GraphicsPipeline?
+        // FIXME: Validate that index |index| of the graphics pipeline's descriptor layout matches the specified descriptors.
+        const auto descriptorSetAllocateInfo = vk::DescriptorSetAllocateInfo().setDescriptorPool(descriptorPool).setDescriptorSetCount(1).setPSetLayouts(&descriptorSetLayout);
+        auto descriptorSets = device.allocateDescriptorSetsUnique(descriptorSetAllocateInfo);
+        this->descriptorSets.emplace_back(std::move(descriptorSets[0]));
+        auto descriptorSet = *this->descriptorSets.back();
+        std::vector<vk::WriteDescriptorSet> writeDescriptorSets = resourceVisitor.releaseWriteDescriptorSets();
+        std::size_t imageIndex = 0;
+        std::size_t bufferIndex = 0;
+        for (auto& writeDescriptorSet : writeDescriptorSets) {
+            switch (writeDescriptorSet.descriptorType) {
+            case vk::DescriptorType::eSampledImage:
+                writeDescriptorSet.setPImageInfo(&resourceVisitor.getDescriptorImageInfos()[imageIndex++]);
+                break;
+            case vk::DescriptorType::eSampler:
+                writeDescriptorSet.setPImageInfo(&resourceVisitor.getDescriptorImageInfos()[imageIndex++]);
+                break;
+            case vk::DescriptorType::eUniformBuffer:
+                writeDescriptorSet.setPBufferInfo(&resourceVisitor.getDescriptorBufferInfos()[bufferIndex++]);
+                break;
+            case vk::DescriptorType::eStorageBuffer:
+                writeDescriptorSet.setPBufferInfo(&resourceVisitor.getDescriptorBufferInfos()[bufferIndex++]);
+                break;
+            }
+            writeDescriptorSet.setDstSet(descriptorSet);
+        }
+        device.updateDescriptorSets(writeDescriptorSets, {});
+        assert(currentPipelineLayout != vk::PipelineLayout());
+        commandBuffer->bindDescriptorSets(pipelineBindPoint, currentPipelineLayout, index, { descriptorSet }, {});
+
+        for (auto& buffer : resourceVisitor.getBuffers())
+            insertBuffer(buffer);
+        for (auto& texture : resourceVisitor.getTextures())
+            insertTexture(texture);
+        for (auto& sampler : resourceVisitor.getSamplers())
+            insertSampler(sampler);
+    }
+
+    void PassImpl::insertBuffer(BufferImpl& buffer) {
+        ResourceReference<BufferImpl> reference(buffer);
+        if (buffers.find(reference) == buffers.end())
+            buffers.insert(std::move(reference));
+    }
+
+    void PassImpl::insertTexture(TextureImpl& texture) {
+        ResourceReference<TextureImpl> reference(texture);
+        if (textures.find(reference) == textures.end())
+            textures.insert(std::move(reference));
+    }
+
+    void PassImpl::insertSampler(SamplerImpl& sampler) {
+        ResourceReference<SamplerImpl> reference(sampler);
+        if (samplers.find(reference) == samplers.end())
+            samplers.insert(std::move(reference));
+    }
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/PassImpl.h
new file mode 100644 (file)
index 0000000..38c4f84
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include "BufferImpl.h"
+#include "TextureImpl.h"
+#include "SamplerImpl.h"
+#include <unordered_set>
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class PassImpl {
+    public:
+        PassImpl() = default;
+        virtual ~PassImpl() = default;
+        PassImpl(const PassImpl&) = delete;
+        PassImpl(PassImpl&&) = default;
+        PassImpl& operator=(const PassImpl&) = delete;
+        PassImpl& operator=(PassImpl&&) = default;
+
+        PassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer);
+
+        vk::CommandBuffer getCommandBuffer() const { return *commandBuffer; }
+
+        template<typename T>
+        void iterateBuffers(T t) {
+            for (const auto& buffer : buffers)
+                t(buffer.get());
+        }
+
+        template<typename T>
+        void iterateTextures(T t) {
+            for (const auto& texture : textures)
+                t(texture.get());
+        }
+
+    protected:
+        void setResources(vk::PipelineBindPoint pipelineBindPoint, unsigned int index, const std::vector<WebGPU::ResourceReference>&);
+        void insertBuffer(BufferImpl& buffer);
+        void insertTexture(TextureImpl& texture);
+        void insertSampler(SamplerImpl& sampler);
+
+        template<typename T>
+        struct ResourceReference {
+            ResourceReference() = delete;
+            ResourceReference(T& resource) : resource(&resource) {
+                resource.incrementReferenceCount();
+            }
+            ~ResourceReference() {
+                if (resource != nullptr)
+                    resource->decrementReferenceCount();
+            }
+            ResourceReference(const ResourceReference&) = delete;
+            ResourceReference(ResourceReference&& resourceReference) : resource(&resourceReference.release()) {
+            }
+            ResourceReference& operator=(const ResourceReference&) = delete;
+            ResourceReference& operator=(ResourceReference&& resourceReference) {
+                if (resource != nullptr)
+                    resource->decrementReferenceCount();
+                resource = &resourceReference.release();
+            }
+
+            bool operator==(const ResourceReference<T>& other) const {
+                return resource == other.resource;
+            }
+
+            T& get() const {
+                return *resource;
+            }
+
+            T& release() {
+                T& result = *resource;
+                resource = nullptr;
+                return result;
+            }
+
+            T* resource;
+        };
+
+        template<typename T>
+        struct ResourceReferenceHash {
+            std::size_t operator()(const ResourceReference<T>& resourceReference) const {
+                std::hash<T*> hash;
+                return hash(&resourceReference.get());
+            }
+        };
+
+        vk::Device device;
+        vk::PipelineLayout currentPipelineLayout;
+        vk::UniqueCommandBuffer commandBuffer;
+        std::vector<vk::UniqueDescriptorPool> descriptorPools;
+        std::vector<vk::UniqueDescriptorSetLayout> descriptorSetLayouts;
+        std::vector<vk::UniqueDescriptorSet> descriptorSets;
+        std::unordered_set<ResourceReference<BufferImpl>, ResourceReferenceHash<BufferImpl>> buffers;
+        std::unordered_set<ResourceReference<TextureImpl>, ResourceReferenceHash<TextureImpl>> textures;
+        std::unordered_set<ResourceReference<SamplerImpl>, ResourceReferenceHash<SamplerImpl>> samplers;
+    };
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.cpp
new file mode 100644 (file)
index 0000000..8818d4a
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "QueueImpl.h"
+#include "TextureImpl.h"
+#include "RenderPassImpl.h"
+#include "ComputePassImpl.h"
+#include "BlitPassImpl.h"
+#include "HostAccessPassImpl.h"
+#include <array>
+#include <cassert>
+#include <boost/optional.hpp>
+
+namespace WebGPU {
+    QueueImpl::QueueImpl(vk::Device device, vk::CommandPool commandPool, std::vector<vk::UniqueImageView>& deviceImageViews, vk::SwapchainKHR swapchain, vk::Extent2D size, vk::Format swapchainFormat, vk::Queue&& queue) : device(device), deviceImageViews(deviceImageViews), swapchain(swapchain), commandPool(commandPool), size(size), swapchainFormat(swapchainFormat), queue(std::move(queue)) {
+        assert(deviceImageViews.size() > activeSemaphoreIndex);
+        for (std::size_t i = 0; i < deviceImageViews.size(); ++i) {
+            swapchainBufferSemaphores.push_back({});
+            swapchainBufferFences.push_back({});
+            activePasses.push_back({});
+            fixupCommandBuffers.push_back({});
+            hostAccessIndices.push_back({});
+        }
+        prepareCurrentFrame();
+    }
+
+    void QueueImpl::prepareCurrentFrame() {
+        auto& currentSemaphoreVector = swapchainBufferSemaphores[activeSemaphoreIndex];
+        currentSemaphoreVector.clear();
+        swapchainBufferFences[activeSemaphoreIndex].clear();
+        activePasses[activeSemaphoreIndex].clear();
+        fixupCommandBuffers[activeSemaphoreIndex].clear();
+        hostAccessIndices[activeSemaphoreIndex].clear();
+        currentSemaphoreVector.emplace_back(device.createSemaphoreUnique(vk::SemaphoreCreateInfo()));
+        auto result = device.acquireNextImageKHR(swapchain, std::numeric_limits<uint64_t>::max(), *currentSemaphoreVector[0], vk::Fence());
+        assert(result.result == vk::Result::eSuccess);
+        activeSwapchainIndex = result.value;
+    }
+
+    vk::UniqueRenderPass QueueImpl::createSpecificRenderPass(const std::vector<vk::ImageView>& imageViews, const std::vector<vk::Format>& formats) {
+        std::vector<vk::AttachmentDescription> attachmentDescriptions;
+        std::vector<vk::AttachmentReference> attachmentReferences;
+        for (std::size_t i = 0; i < imageViews.size(); ++i) {
+            auto attachmentDescription = vk::AttachmentDescription()
+                .setFormat(formats[i])
+                .setSamples(vk::SampleCountFlagBits::e1)
+                .setLoadOp(vk::AttachmentLoadOp::eClear)
+                .setStoreOp(vk::AttachmentStoreOp::eStore)
+                .setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
+                .setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
+                .setInitialLayout(vk::ImageLayout::eUndefined)
+                .setFinalLayout(vk::ImageLayout::ePresentSrcKHR);
+            attachmentDescriptions.emplace_back(std::move(attachmentDescription));
+
+            auto attachmentReference = vk::AttachmentReference().setAttachment(static_cast<uint32_t>(i)).setLayout(vk::ImageLayout::eColorAttachmentOptimal);
+            attachmentReferences.emplace_back(std::move(attachmentReference));
+        }
+
+        const auto subpassDescription = vk::SubpassDescription()
+            .setPipelineBindPoint(vk::PipelineBindPoint::eGraphics)
+            .setInputAttachmentCount(0)
+            .setPInputAttachments(nullptr)
+            .setColorAttachmentCount(static_cast<uint32_t>(attachmentReferences.size()))
+            .setPColorAttachments(attachmentReferences.data())
+            .setPResolveAttachments(nullptr)
+            .setPDepthStencilAttachment(nullptr)
+            .setPreserveAttachmentCount(0)
+            .setPPreserveAttachments(nullptr);
+
+        const auto renderPassCreateInfo = vk::RenderPassCreateInfo()
+            .setAttachmentCount(static_cast<uint32_t>(attachmentDescriptions.size()))
+            .setPAttachments(attachmentDescriptions.data())
+            .setSubpassCount(1)
+            .setPSubpasses(&subpassDescription)
+            .setDependencyCount(0)
+            .setPDependencies(nullptr);
+        return device.createRenderPassUnique(renderPassCreateInfo);
+    }
+
+    vk::UniqueFramebuffer QueueImpl::createFramebuffer(vk::RenderPass renderpass, const std::vector<vk::ImageView>& imageViews) {
+        const auto framebufferCreateInfo = vk::FramebufferCreateInfo()
+            .setRenderPass(renderpass)
+            .setAttachmentCount(static_cast<uint32_t>(imageViews.size()))
+            .setPAttachments(imageViews.data())
+            .setWidth(size.width)
+            .setHeight(size.height)
+            .setLayers(1);
+        return device.createFramebufferUnique(framebufferCreateInfo);
+    }
+
+    std::unique_ptr<RenderPass> QueueImpl::createRenderPass(const std::vector<std::reference_wrapper<Texture>>* textures) {
+        std::vector<vk::ImageView> imageViews;
+        std::vector<vk::ClearValue> clearValues;
+        std::vector<vk::Format> formats;
+        std::vector<std::reference_wrapper<TextureImpl>> destinationTextures;
+        if (textures == nullptr) {
+            imageViews.emplace_back(*deviceImageViews[activeSwapchainIndex]);
+            clearValues.emplace_back(vk::ClearColorValue(std::array<float, 4>({ 0.0f, 0.0f, 0.0f, 1.0f })));
+            formats.push_back(swapchainFormat);
+        }
+        else {
+            for (auto& textureWrapper : *textures) {
+                Texture& texture = textureWrapper;
+                auto downcast = dynamic_cast<TextureImpl*>(&texture);
+                assert(downcast != nullptr);
+                TextureImpl& downcastedTexture = *downcast;
+                destinationTextures.emplace_back(downcastedTexture);
+                imageViews.emplace_back(downcastedTexture.getImageView());
+                clearValues.emplace_back(vk::ClearColorValue(std::array<float, 4>({ 0.0f, 0.0f, 0.0f, 1.0f })));
+                formats.emplace_back(downcastedTexture.getFormat());
+            }
+        }
+
+        const auto commandBufferAllocateInfo = vk::CommandBufferAllocateInfo().setCommandPool(commandPool).setLevel(vk::CommandBufferLevel::ePrimary).setCommandBufferCount(1);
+        auto commandBuffers = device.allocateCommandBuffersUnique(commandBufferAllocateInfo);
+        auto& commandBuffer = commandBuffers[0];
+        const auto commandBufferBeginInfo = vk::CommandBufferBeginInfo().setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+        commandBuffer->begin(commandBufferBeginInfo);
+        auto renderPass = createSpecificRenderPass(imageViews, formats);
+        auto framebuffer = createFramebuffer(*renderPass, imageViews);
+        const auto renderPassBeginInfo = vk::RenderPassBeginInfo()
+            .setRenderPass(*renderPass)
+            .setFramebuffer(*framebuffer)
+            .setRenderArea(vk::Rect2D(vk::Offset2D(0, 0), size))
+            .setClearValueCount(static_cast<uint32_t>(clearValues.size()))
+            .setPClearValues(clearValues.data());
+        commandBuffer->beginRenderPass(renderPassBeginInfo, vk::SubpassContents::eInline);
+        return std::unique_ptr<RenderPass>(new RenderPassImpl(device, std::move(commandBuffer), std::move(framebuffer), std::move(renderPass), destinationTextures));
+    }
+
+    void QueueImpl::commitRenderPass(std::unique_ptr<RenderPass>&& renderPass) {
+        auto* downcast = dynamic_cast<PassImpl*>(renderPass.get());
+        assert(downcast != nullptr);
+        renderPass.release();
+        std::unique_ptr<PassImpl> pass(downcast);
+
+        commitPass(*pass);
+
+        activePasses[activeSemaphoreIndex].emplace_back(std::move(pass));
+    }
+
+    std::unique_ptr<ComputePass> QueueImpl::createComputePass() {
+        const auto commandBufferAllocateInfo = vk::CommandBufferAllocateInfo().setCommandPool(commandPool).setLevel(vk::CommandBufferLevel::ePrimary).setCommandBufferCount(1);
+        auto commandBuffers = device.allocateCommandBuffersUnique(commandBufferAllocateInfo);
+        auto& commandBuffer = commandBuffers[0];
+        const auto commandBufferBeginInfo = vk::CommandBufferBeginInfo().setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+        commandBuffer->begin(commandBufferBeginInfo);
+        return std::unique_ptr<ComputePass>(new ComputePassImpl(device, std::move(commandBuffer)));
+    }
+
+    void QueueImpl::commitComputePass(std::unique_ptr<ComputePass>&& computePass) {
+        auto* downcast = dynamic_cast<PassImpl*>(computePass.get());
+        assert(downcast != nullptr);
+        computePass.release();
+        std::unique_ptr<PassImpl> pass(downcast);
+
+        commitPass(*pass);
+
+        activePasses[activeSemaphoreIndex].emplace_back(std::move(pass));
+    }
+
+    std::unique_ptr<BlitPass> QueueImpl::createBlitPass() {
+        const auto commandBufferAllocateInfo = vk::CommandBufferAllocateInfo().setCommandPool(commandPool).setLevel(vk::CommandBufferLevel::ePrimary).setCommandBufferCount(1);
+        auto commandBuffers = device.allocateCommandBuffersUnique(commandBufferAllocateInfo);
+        auto& commandBuffer = commandBuffers[0];
+        const auto commandBufferBeginInfo = vk::CommandBufferBeginInfo().setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+        commandBuffer->begin(commandBufferBeginInfo);
+        return std::unique_ptr<BlitPass>(new BlitPassImpl(device, std::move(commandBuffer)));
+    }
+
+    void QueueImpl::commitBlitPass(std::unique_ptr<BlitPass>&& blitPass) {
+        auto* downcast = dynamic_cast<PassImpl*>(blitPass.get());
+        assert(downcast != nullptr);
+        blitPass.release();
+        std::unique_ptr<PassImpl> pass(downcast);
+
+        commitPass(*pass);
+
+        activePasses[activeSemaphoreIndex].emplace_back(std::move(pass));
+    }
+
+    std::unique_ptr<HostAccessPass> QueueImpl::createHostAccessPass() {
+        const auto commandBufferAllocateInfo = vk::CommandBufferAllocateInfo().setCommandPool(commandPool).setLevel(vk::CommandBufferLevel::ePrimary).setCommandBufferCount(1);
+        auto commandBuffers = device.allocateCommandBuffersUnique(commandBufferAllocateInfo);
+        auto& commandBuffer = commandBuffers[0];
+        const auto commandBufferBeginInfo = vk::CommandBufferBeginInfo().setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+        commandBuffer->begin(commandBufferBeginInfo);
+        return std::unique_ptr<HostAccessPass>(new HostAccessPassImpl(device, std::move(commandBuffer)));
+    }
+
+    void QueueImpl::commitHostAccessPass(std::unique_ptr<HostAccessPass>&& hostAccessPass) {
+        {
+            auto* downcast = dynamic_cast<HostAccessPassImpl*>(hostAccessPass.get());
+            assert(downcast != nullptr);
+            auto& pass = *downcast;
+            // FIXME: Asking the GPU to wait on the CPU causes my (NVidia) driver to crash inside vkQueuePresentKHR().
+            // However, I don't think this is a structural problem; we could just as easily have created a new resource
+            // and used the transfer queue to copy the contents.
+            // pass.getCommandBuffer().waitEvents({ pass.getFinishedEvent() }, vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands, {}, {}, {});
+        }
+
+        auto* downcast = dynamic_cast<PassImpl*>(hostAccessPass.get());
+        assert(downcast != nullptr);
+        hostAccessPass.release();
+        std::unique_ptr<PassImpl> pass(downcast);
+
+        hostAccessIndices[activeSemaphoreIndex].push_back(activePasses[activeSemaphoreIndex].size());
+
+        commitPass(*pass);
+
+        activePasses[activeSemaphoreIndex].emplace_back(std::move(pass));
+    }
+
+    void QueueImpl::present() {
+        vk::Semaphore waitSemaphore = *swapchainBufferSemaphores[activeSemaphoreIndex].back();
+        vk::Result result;
+        const auto presentInfo = vk::PresentInfoKHR()
+            .setWaitSemaphoreCount(1)
+            .setPWaitSemaphores(&waitSemaphore)
+            .setSwapchainCount(1)
+            .setPSwapchains(&swapchain)
+            .setPImageIndices(&activeSwapchainIndex)
+            .setPResults(&result);
+        auto result2 = queue.presentKHR(presentInfo);
+        assert(result == vk::Result::eSuccess);
+        assert(result2 == vk::Result::eSuccess);
+
+        activeSemaphoreIndex = (activeSemaphoreIndex + 1) % deviceImageViews.size();
+
+        assert(swapchainBufferFences[activeSemaphoreIndex].size() == activePasses[activeSemaphoreIndex].size());
+        std::vector<vk::Fence> fences;
+        std::size_t index = 0;
+        for (std::size_t hostAccessIndex : hostAccessIndices[activeSemaphoreIndex]) {
+            for (; index < hostAccessIndex; ++index)
+                fences.push_back(*swapchainBufferFences[activeSemaphoreIndex][index]);
+            result = device.waitForFences(fences, VK_TRUE, std::numeric_limits<uint64_t>::max());
+            device.resetFences(fences);
+            assert(result == vk::Result::eSuccess);
+            fences.clear();
+
+            auto* downcast = dynamic_cast<HostAccessPassImpl*>(activePasses[activeSemaphoreIndex][index].get());
+            assert(downcast);
+            auto& hostAccessPass = *downcast;
+            hostAccessPass.execute();
+            device.setEvent(hostAccessPass.getFinishedEvent());
+        }
+        for (; index < swapchainBufferFences[activeSemaphoreIndex].size(); ++index)
+            fences.push_back(*swapchainBufferFences[activeSemaphoreIndex][index]);
+        result = device.waitForFences(fences, VK_TRUE, std::numeric_limits<uint64_t>::max());
+        device.resetFences(fences);
+        assert(result == vk::Result::eSuccess);
+
+        prepareCurrentFrame();
+    }
+   
+    void QueueImpl::commitPass(PassImpl& pass) {
+        pass.getCommandBuffer().end();
+        const vk::PipelineStageFlags waitStageMask[] = { vk::PipelineStageFlagBits::eTopOfPipe };
+        vk::Semaphore waitSemaphore = *swapchainBufferSemaphores[activeSemaphoreIndex].back();
+        swapchainBufferSemaphores[activeSemaphoreIndex].emplace_back(device.createSemaphoreUnique(vk::SemaphoreCreateInfo()));
+        vk::Semaphore signalSemaphore = *swapchainBufferSemaphores[activeSemaphoreIndex].back();
+        swapchainBufferFences[activeSemaphoreIndex].push_back(device.createFenceUnique(vk::FenceCreateInfo()));
+        vk::Fence signalFence = *swapchainBufferFences[activeSemaphoreIndex].back();
+
+        // FIXME: We may want to defer this to Present time in order to coalesce the barriers better than just doing them lazily here.
+        // FIXME: Compute-only workflows may never call present, which means we need some sort of flush() call. Or we can just say that
+        // present() is the same thing as flush, and is smart enough to not actually present if it isn't hooked up to a display.
+        auto fixup1 = synchronizeResources(pass);
+        auto commandBuffer = pass.getCommandBuffer();
+        // FIXME: This is done eagerly because the API may want to update the contents of a resource from the CPU.
+        // Instead, we should schedule this update along with the queue so we can insert the correct barriers in front of it.
+        auto fixup2 = synchronizeResources(pass);
+
+        vk::CommandBuffer commandBuffers[] = { fixup1, commandBuffer, fixup2 };
+
+        auto submitInfo = vk::SubmitInfo()
+            .setWaitSemaphoreCount(1)
+            .setPWaitSemaphores(&waitSemaphore)
+            .setPWaitDstStageMask(waitStageMask)
+            .setCommandBufferCount(3)
+            .setPCommandBuffers(commandBuffers)
+            .setSignalSemaphoreCount(1)
+            .setPSignalSemaphores(&signalSemaphore);
+        queue.submit({ submitInfo }, signalFence);
+    }
+
+    vk::CommandBuffer QueueImpl::synchronizeResources(PassImpl& pass) {
+        const auto commandBufferAllocateInfo = vk::CommandBufferAllocateInfo().setCommandPool(commandPool).setLevel(vk::CommandBufferLevel::ePrimary).setCommandBufferCount(1);
+        auto commandBuffers = device.allocateCommandBuffersUnique(commandBufferAllocateInfo);
+        auto& commandBuffer = commandBuffers[0];
+        const auto commandBufferBeginInfo = vk::CommandBufferBeginInfo().setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
+        commandBuffer->begin(commandBufferBeginInfo);
+        // FIXME: Issue smaller, cheaper, more specific barriers than these.
+        std::vector<vk::BufferMemoryBarrier> bufferMemoryBarriers;
+        pass.iterateBuffers([&](BufferImpl& buffer) {
+            bufferMemoryBarriers.emplace_back(vk::BufferMemoryBarrier()
+                .setSrcAccessMask(vk::AccessFlagBits::eIndirectCommandRead | vk::AccessFlagBits::eIndexRead | vk::AccessFlagBits::eVertexAttributeRead | vk::AccessFlagBits::eUniformRead | vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eHostRead | vk::AccessFlagBits::eHostWrite)
+                .setDstAccessMask(vk::AccessFlagBits::eIndirectCommandRead | vk::AccessFlagBits::eIndexRead | vk::AccessFlagBits::eVertexAttributeRead | vk::AccessFlagBits::eUniformRead | vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eHostRead | vk::AccessFlagBits::eHostWrite)
+                .setSrcQueueFamilyIndex(0)
+                .setDstQueueFamilyIndex(0)
+                .setBuffer(buffer.getBuffer())
+                .setOffset(0)
+                .setSize(VK_WHOLE_SIZE));
+        });
+        std::vector<vk::ImageMemoryBarrier> imageMemoryBarriers;
+        pass.iterateTextures([&](TextureImpl& texture) {
+            imageMemoryBarriers.emplace_back(vk::ImageMemoryBarrier()
+                .setSrcAccessMask(vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eHostRead | vk::AccessFlagBits::eHostWrite)
+                .setDstAccessMask(vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eHostRead | vk::AccessFlagBits::eHostWrite)
+                .setOldLayout(texture.getTransferredToGPU() ? vk::ImageLayout::eGeneral : vk::ImageLayout::ePreinitialized)
+                .setNewLayout(vk::ImageLayout::eGeneral)
+                .setSrcQueueFamilyIndex(0)
+                .setDstQueueFamilyIndex(0)
+                .setImage(texture.getImage())
+                .setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)));
+            texture.setTransferredToGPU(true);
+        });
+        commandBuffer->pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands, vk::DependencyFlagBits(), {}, bufferMemoryBarriers, imageMemoryBarriers);
+        commandBuffer->end();
+
+        auto& frameCommandBuffers = fixupCommandBuffers[activeSemaphoreIndex];
+        frameCommandBuffers.push_back(std::move(commandBuffer));
+        return *frameCommandBuffers.back();
+    }
+
+    QueueImpl::~QueueImpl() {
+        for (std::size_t i = 0; i < deviceImageViews.size(); ++i) {
+            activeSemaphoreIndex = (activeSemaphoreIndex + 1) % deviceImageViews.size();
+
+            assert(swapchainBufferFences[activeSemaphoreIndex].size() == activePasses[activeSemaphoreIndex].size());
+            std::vector<vk::Fence> fences;
+            for (std::size_t index = 0; index < swapchainBufferFences[activeSemaphoreIndex].size(); ++index)
+                fences.push_back(*swapchainBufferFences[activeSemaphoreIndex][index]);
+            auto result = device.waitForFences(fences, VK_TRUE, std::numeric_limits<uint64_t>::max());
+            device.resetFences(fences);
+            assert(result == vk::Result::eSuccess);
+
+            swapchainBufferSemaphores[activeSemaphoreIndex].clear();
+            swapchainBufferFences[activeSemaphoreIndex].clear();
+            activePasses[activeSemaphoreIndex].clear();
+            fixupCommandBuffers[activeSemaphoreIndex].clear();
+            hostAccessIndices[activeSemaphoreIndex].clear();
+        }
+    }
+}
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/QueueImpl.h
new file mode 100644 (file)
index 0000000..bba1626
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include "RenderPassImpl.h"
+#include <memory>
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class QueueImpl : public Queue {
+    public:
+        QueueImpl() = default;
+        virtual ~QueueImpl();
+        QueueImpl(const QueueImpl&) = delete;
+        QueueImpl(QueueImpl&&) = default;
+        QueueImpl& operator=(const QueueImpl&) = delete;
+        QueueImpl& operator=(QueueImpl&&) = default;
+
+        QueueImpl(vk::Device device, vk::CommandPool commandPool, std::vector<vk::UniqueImageView>& deviceImageViews, vk::SwapchainKHR swapchain, vk::Extent2D size, vk::Format swapchainFormat, vk::Queue&& queue);
+
+    private:
+        std::unique_ptr<RenderPass> createRenderPass(const std::vector<std::reference_wrapper<Texture>>* textures) override;
+        void commitRenderPass(std::unique_ptr<RenderPass>&&) override;
+        std::unique_ptr<ComputePass> createComputePass() override;
+        void commitComputePass(std::unique_ptr<ComputePass>&&) override;
+        std::unique_ptr<BlitPass> createBlitPass() override;
+        void commitBlitPass(std::unique_ptr<BlitPass>&&) override;
+        std::unique_ptr<HostAccessPass> createHostAccessPass() override;
+        void commitHostAccessPass(std::unique_ptr<HostAccessPass>&&) override;
+        void present() override;
+
+        vk::UniqueRenderPass createSpecificRenderPass(const std::vector<vk::ImageView>& imageViews, const std::vector<vk::Format>& formats);
+        vk::UniqueFramebuffer createFramebuffer(vk::RenderPass renderpass, const std::vector<vk::ImageView>& imageViews);
+        void prepareCurrentFrame();
+        vk::CommandBuffer synchronizeResources(PassImpl& pass);
+        void commitPass(PassImpl&);
+
+        uint32_t activeSemaphoreIndex{ 0 };
+        uint32_t activeSwapchainIndex;
+        vk::Device device;
+        vk::CommandPool commandPool;
+        std::vector<vk::UniqueImageView>& deviceImageViews;
+        vk::SwapchainKHR swapchain;
+        std::vector<std::vector<vk::UniqueSemaphore>> swapchainBufferSemaphores;
+        std::vector<std::vector<vk::UniqueFence>> swapchainBufferFences;
+        std::vector<std::vector<std::unique_ptr<PassImpl>>> activePasses;
+        std::vector<std::vector<vk::UniqueCommandBuffer>> fixupCommandBuffers;
+        std::vector<std::vector<std::size_t>> hostAccessIndices;
+        vk::Extent2D size;
+        vk::Format swapchainFormat;
+        vk::Queue queue;
+    };
+}
+
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.cpp
new file mode 100644 (file)
index 0000000..b2e413a
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "RenderPassImpl.h"
+#include "RenderStateImpl.h"
+#include "BufferImpl.h"
+#include "TextureImpl.h"
+#include <cassert>
+
+namespace WebGPU {
+    RenderPassImpl::RenderPassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer, vk::UniqueFramebuffer&& framebuffer, vk::UniqueRenderPass&& renderPass, const std::vector<std::reference_wrapper<TextureImpl>>& destinationTextures) : PassImpl(device, std::move(commandBuffer)), framebuffer(std::move(framebuffer)), renderPass(std::move(renderPass)) {
+        for (auto& destinationTexture : destinationTextures) {
+            insertTexture(destinationTexture.get());
+        }
+    }
+
+    void RenderPassImpl::setRenderState(const RenderState& renderState) {
+        auto* downcast = dynamic_cast<const RenderStateImpl*>(&renderState);
+        assert(downcast != nullptr);
+        auto& state = *downcast;
+        currentPipelineLayout = state.getPipelineLayout();
+        commandBuffer->bindPipeline(vk::PipelineBindPoint::eGraphics, state.getPipeline());
+    }
+
+    void RenderPassImpl::setVertexAttributeBuffers(const std::vector<std::reference_wrapper<Buffer>>& buffers) {
+        std::vector<vk::Buffer> vulkanBuffers;
+        std::vector<vk::DeviceSize> offsets;
+        for (const auto& buffer : buffers) {
+            auto* downcast = dynamic_cast<BufferImpl*>(&buffer.get());
+            assert(downcast != nullptr);
+            auto& buf = *downcast;
+            vulkanBuffers.push_back(buf.getBuffer());
+            offsets.push_back(0);
+            insertBuffer(buf);
+        }
+        commandBuffer->bindVertexBuffers(0, vulkanBuffers, offsets);
+    }
+
+    void RenderPassImpl::setResources(unsigned int index, const std::vector<WebGPU::ResourceReference>& resourceReferences) {
+        PassImpl::setResources(vk::PipelineBindPoint::eGraphics, index, resourceReferences);
+    }
+
+    void RenderPassImpl::setViewport(unsigned int x, unsigned int y, unsigned int width, unsigned int height) {
+        auto const viewport = vk::Viewport().setX(static_cast<float>(x)).setY(static_cast<float>(y)).setWidth(static_cast<float>(width)).setHeight(static_cast<float>(height)).setMinDepth(0.0f).setMaxDepth(1.0f);
+        commandBuffer->setViewport(0, 1, &viewport);
+    }
+
+    void RenderPassImpl::setScissorRect(unsigned int x, unsigned int y, unsigned int width, unsigned int height) {
+        vk::Rect2D const scissor(vk::Offset2D(x, y), vk::Extent2D(width, height));
+        commandBuffer->setScissor(0, 1, &scissor);
+    }
+
+    void RenderPassImpl::draw(unsigned int vertexCount) {
+        commandBuffer->draw(vertexCount, 1, 0, 0);
+    }
+}
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderPassImpl.h
new file mode 100644 (file)
index 0000000..d233deb
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include "PassImpl.h"
+#include "BufferImpl.h"
+#include "TextureImpl.h"
+#include <unordered_set>
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class RenderPassImpl : public PassImpl, public RenderPass {
+    public:
+        RenderPassImpl() = default;
+        virtual ~RenderPassImpl() = default;
+        RenderPassImpl(const RenderPassImpl&) = delete;
+        RenderPassImpl(RenderPassImpl&&) = default;
+        RenderPassImpl& operator=(const RenderPassImpl&) = delete;
+        RenderPassImpl& operator=(RenderPassImpl&&) = default;
+
+        RenderPassImpl(vk::Device device, vk::UniqueCommandBuffer&& commandBuffer, vk::UniqueFramebuffer&& framebuffer, vk::UniqueRenderPass&& renderPass, const std::vector<std::reference_wrapper<TextureImpl>>& destinationTextures);
+
+    private:
+        void setRenderState(const RenderState& renderState) override;
+        void setVertexAttributeBuffers(const std::vector<std::reference_wrapper<Buffer>>& buffers) override;
+        void setResources(unsigned int index, const std::vector<WebGPU::ResourceReference>&) override;
+        void setViewport(unsigned int x, unsigned int y, unsigned int width, unsigned int height) override;
+        void setScissorRect(unsigned int x, unsigned int y, unsigned int width, unsigned int height) override;
+        void draw(unsigned int vertexCount) override;
+
+        vk::UniqueFramebuffer framebuffer;
+        vk::UniqueRenderPass renderPass;
+    };
+}
+
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.cpp
new file mode 100644 (file)
index 0000000..bd7cf60
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "RenderStateImpl.h"
+
+namespace WebGPU {
+    RenderStateImpl::RenderStateImpl(vk::UniquePipeline&& pipeline, std::vector<vk::UniqueDescriptorSetLayout>&& descriptorSetLayouts, vk::UniquePipelineLayout&& pipelineLayout, vk::UniqueRenderPass&& compatibleRenderPass) : pipeline(std::move(pipeline)), descriptorSetLayouts(std::move(descriptorSetLayouts)), pipelineLayout(std::move(pipelineLayout)), compatibleRenderPass(std::move(compatibleRenderPass)) {
+    }
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/RenderStateImpl.h
new file mode 100644 (file)
index 0000000..3230277
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include <vector>
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class RenderStateImpl : public RenderState {
+    public:
+        RenderStateImpl() = default;
+        virtual ~RenderStateImpl() = default;
+        RenderStateImpl(const RenderStateImpl&) = delete;
+        RenderStateImpl(RenderStateImpl&&) = default;
+        RenderStateImpl& operator=(const RenderStateImpl&) = delete;
+        RenderStateImpl& operator=(RenderStateImpl&&) = default;
+
+        RenderStateImpl(vk::UniquePipeline&& pipeline, std::vector<vk::UniqueDescriptorSetLayout>&& descriptorSetLayouts, vk::UniquePipelineLayout&& pipelineLayout, vk::UniqueRenderPass&& compatibleRenderPass);
+
+        vk::Pipeline getPipeline() const { return *pipeline; }
+        vk::PipelineLayout getPipelineLayout() const { return *pipelineLayout; }
+        const std::vector<vk::UniqueDescriptorSetLayout>& getDescriptorSetLayouts() const { return descriptorSetLayouts; }
+
+    private:
+        vk::UniquePipeline pipeline;
+        std::vector<vk::UniqueDescriptorSetLayout> descriptorSetLayouts;
+        vk::UniquePipelineLayout pipelineLayout;
+        vk::UniqueRenderPass compatibleRenderPass;
+    };
+}
+
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.cpp
new file mode 100644 (file)
index 0000000..c615e89
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "SamplerImpl.h"
+
+namespace WebGPU {
+    SamplerImpl::SamplerImpl(std::function<void(SamplerImpl&)>&& returnToDevice, vk::Filter filter, vk::SamplerMipmapMode mipmapMode, vk::SamplerAddressMode addressMode, vk::Device device, vk::UniqueSampler&& sampler) : returnToDevice(std::move(returnToDevice)), device(device), sampler(std::move(sampler)), filter(filter), mipmapMode(mipmapMode), addressMode(addressMode) {
+    }
+
+    void SamplerImpl::decrementReferenceCount() {
+        if (--referenceCount == 0)
+            returnToDevice(*this);
+    }
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/SamplerImpl.h
new file mode 100644 (file)
index 0000000..fd21552
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class SamplerImpl : public Sampler {
+    public:
+        SamplerImpl() = default;
+        virtual ~SamplerImpl() = default;
+        SamplerImpl(const SamplerImpl&) = delete;
+        SamplerImpl(SamplerImpl&&) = default;
+        SamplerImpl& operator=(const SamplerImpl&) = delete;
+        SamplerImpl& operator=(SamplerImpl&&) = default;
+
+        SamplerImpl(std::function<void(SamplerImpl&)>&& returnToDevice, vk::Filter filter, vk::SamplerMipmapMode mipmapMode, vk::SamplerAddressMode addressMode, vk::Device device, vk::UniqueSampler&& sampler);
+
+        vk::Sampler getSampler() { return *sampler; }
+        void incrementReferenceCount() { ++referenceCount; }
+        void decrementReferenceCount();
+        vk::Filter getFilter() { return filter; }
+        vk::SamplerMipmapMode getMipmapMode() { return mipmapMode; }
+        vk::SamplerAddressMode getAddressMode() { return addressMode; }
+
+    private:
+        std::function<void(SamplerImpl&)> returnToDevice;
+        vk::Device device;
+        vk::UniqueSampler sampler;
+        unsigned int referenceCount{ 0 };
+        vk::Filter filter;
+        vk::SamplerMipmapMode mipmapMode;
+        vk::SamplerAddressMode addressMode;
+    };
+}
+
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.cpp b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.cpp
new file mode 100644 (file)
index 0000000..b46d86a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "TextureImpl.h"
+
+namespace WebGPU {
+    TextureImpl::TextureImpl(std::function<void(TextureImpl&)>&& returnToDevice, unsigned int width, unsigned int height, vk::Format format, vk::Device device, vk::UniqueDeviceMemory&& memory, vk::UniqueImage&& image, vk::UniqueImageView&& imageView) : returnToDevice(std::move(returnToDevice)), device(device), memory(std::move(memory)), image(std::move(image)), imageView(std::move(imageView)), format(format), width(width), height(height) {
+    }
+
+    void TextureImpl::decrementReferenceCount() {
+        if (--referenceCount == 0)
+            returnToDevice(*this);
+    }
+}
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.h b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/TextureImpl.h
new file mode 100644 (file)
index 0000000..63cdf86
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+* Copyright (C) 2017 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+*    notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+*    notice, this list of conditions and the following disclaimer in the
+*    documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+#include "WebGPU.h"
+#include <functional>
+#include <vulkan/vulkan.hpp>
+
+namespace WebGPU {
+    class TextureImpl : public Texture {
+    public:
+        TextureImpl() = default;
+        virtual ~TextureImpl() = default;
+        TextureImpl(const TextureImpl&) = delete;
+        TextureImpl(TextureImpl&&) = default;
+        TextureImpl& operator=(const TextureImpl&) = delete;
+        TextureImpl& operator=(TextureImpl&&) = default;
+
+        TextureImpl(std::function<void(TextureImpl&)>&& returnToDevice, unsigned int width, unsigned int height, vk::Format format, vk::Device device, vk::UniqueDeviceMemory&& memory, vk::UniqueImage&& image, vk::UniqueImageView&& imageView);
+
+        vk::Image getImage() const { return *image; }
+        vk::ImageView getImageView() const { return *imageView; }
+        vk::Format getFormat() const { return format; }
+        void incrementReferenceCount() { ++referenceCount; }
+        void decrementReferenceCount();
+        unsigned int getWidth() const { return width; }
+        unsigned int getHeight() const { return height; }
+        bool getTransferredToGPU() const { return transferredToGPU; }
+        void setTransferredToGPU(bool transferredToGPU) { this->transferredToGPU = transferredToGPU; }
+
+    private:
+        std::function<void(TextureImpl&)> returnToDevice;
+        vk::Device device;
+        vk::UniqueDeviceMemory memory;
+        vk::UniqueImage image;
+        vk::UniqueImageView imageView;
+        vk::Format format;
+        unsigned int referenceCount{ 0 };
+        unsigned int width;
+        unsigned int height;
+        bool transferredToGPU{ false };
+    };
+}
+
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj
new file mode 100644 (file)
index 0000000..cbb2911
--- /dev/null
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>15.0</VCProjectVersion>
+    <ProjectGuid>{9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>WebGPUVulkan</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v141</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_LIB;VK_USE_PLATFORM_WIN32_KHR;NOMINMAX;BOOST_ALL_DYN_LINK;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories>..\WebGPU-Common;C:\Users\lithe\Documents\src\boost_1_65_1\prefix\include\boost-1_65_1;C:\Users\lithe\Source\Repos\Vulkan-LoaderAndValidationLayers\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="BlitPassImpl.h" />
+    <ClInclude Include="BufferImpl.h" />
+    <ClInclude Include="ComputePassImpl.h" />
+    <ClInclude Include="ComputeStateImpl.h" />
+    <ClInclude Include="DeviceImpl.h" />
+    <ClInclude Include="HostAccessPassImpl.h" />
+    <ClInclude Include="PassImpl.h" />
+    <ClInclude Include="QueueImpl.h" />
+    <ClInclude Include="RenderPassImpl.h" />
+    <ClInclude Include="RenderStateImpl.h" />
+    <ClInclude Include="SamplerImpl.h" />
+    <ClInclude Include="TextureImpl.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="BlitPassImpl.cpp" />
+    <ClCompile Include="BufferImpl.cpp" />
+    <ClCompile Include="ComputePassImpl.cpp" />
+    <ClCompile Include="ComputeStateImpl.cpp" />
+    <ClCompile Include="DeviceImpl.cpp" />
+    <ClCompile Include="HostAccessPassImpl.cpp" />
+    <ClCompile Include="PassImpl.cpp" />
+    <ClCompile Include="QueueImpl.cpp" />
+    <ClCompile Include="RenderPassImpl.cpp" />
+    <ClCompile Include="RenderStateImpl.cpp" />
+    <ClCompile Include="SamplerImpl.cpp" />
+    <ClCompile Include="TextureImpl.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\WebGPU-Common\WebGPU-Common.vcxproj">
+      <Project>{9e765542-8c51-43b4-b0c0-c166d21cb222}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj.filters b/Tools/WebGPUAPIStructure/WebGPU-Vulkan/WebGPU-Vulkan.vcxproj.filters
new file mode 100644 (file)
index 0000000..a060e6f
--- /dev/null
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="BlitPassImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="BufferImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="ComputePassImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="ComputeStateImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="DeviceImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="HostAccessPassImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="PassImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="QueueImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="RenderPassImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="RenderStateImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="SamplerImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="TextureImpl.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="BlitPassImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="BufferImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="ComputePassImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="ComputeStateImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="DeviceImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="HostAccessPassImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="PassImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="QueueImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="RenderPassImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="RenderStateImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="SamplerImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="TextureImpl.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/Tools/WebGPUAPIStructure/WebGPU.sln b/Tools/WebGPUAPIStructure/WebGPU.sln
new file mode 100644 (file)
index 0000000..89f602f
--- /dev/null
@@ -0,0 +1,57 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27004.2005
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Example", "Example\Example.vcxproj", "{5817155C-AB74-445D-9A61-47CDE42C2BAD}"
+       ProjectSection(ProjectDependencies) = postProject
+               {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D} = {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}
+       EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WebGPU-Common", "WebGPU-Common\WebGPU-Common.vcxproj", "{9E765542-8C51-43B4-B0C0-C166D21CB222}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WebGPU-Vulkan", "WebGPU-Vulkan\WebGPU-Vulkan.vcxproj", "{9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}"
+       ProjectSection(ProjectDependencies) = postProject
+               {9E765542-8C51-43B4-B0C0-C166D21CB222} = {9E765542-8C51-43B4-B0C0-C166D21CB222}
+       EndProjectSection
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|x64 = Debug|x64
+               Debug|x86 = Debug|x86
+               Release|x64 = Release|x64
+               Release|x86 = Release|x86
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {5817155C-AB74-445D-9A61-47CDE42C2BAD}.Debug|x64.ActiveCfg = Debug|x64
+               {5817155C-AB74-445D-9A61-47CDE42C2BAD}.Debug|x64.Build.0 = Debug|x64
+               {5817155C-AB74-445D-9A61-47CDE42C2BAD}.Debug|x86.ActiveCfg = Debug|Win32
+               {5817155C-AB74-445D-9A61-47CDE42C2BAD}.Debug|x86.Build.0 = Debug|Win32
+               {5817155C-AB74-445D-9A61-47CDE42C2BAD}.Release|x64.ActiveCfg = Release|x64
+               {5817155C-AB74-445D-9A61-47CDE42C2BAD}.Release|x64.Build.0 = Release|x64
+               {5817155C-AB74-445D-9A61-47CDE42C2BAD}.Release|x86.ActiveCfg = Release|Win32
+               {5817155C-AB74-445D-9A61-47CDE42C2BAD}.Release|x86.Build.0 = Release|Win32
+               {9E765542-8C51-43B4-B0C0-C166D21CB222}.Debug|x64.ActiveCfg = Debug|x64
+               {9E765542-8C51-43B4-B0C0-C166D21CB222}.Debug|x64.Build.0 = Debug|x64
+               {9E765542-8C51-43B4-B0C0-C166D21CB222}.Debug|x86.ActiveCfg = Debug|Win32
+               {9E765542-8C51-43B4-B0C0-C166D21CB222}.Debug|x86.Build.0 = Debug|Win32
+               {9E765542-8C51-43B4-B0C0-C166D21CB222}.Release|x64.ActiveCfg = Release|x64
+               {9E765542-8C51-43B4-B0C0-C166D21CB222}.Release|x64.Build.0 = Release|x64
+               {9E765542-8C51-43B4-B0C0-C166D21CB222}.Release|x86.ActiveCfg = Release|Win32
+               {9E765542-8C51-43B4-B0C0-C166D21CB222}.Release|x86.Build.0 = Release|Win32
+               {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}.Debug|x64.ActiveCfg = Debug|x64
+               {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}.Debug|x64.Build.0 = Debug|x64
+               {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}.Debug|x86.ActiveCfg = Debug|Win32
+               {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}.Debug|x86.Build.0 = Debug|Win32
+               {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}.Release|x64.ActiveCfg = Release|x64
+               {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}.Release|x64.Build.0 = Release|x64
+               {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}.Release|x86.ActiveCfg = Release|Win32
+               {9B2EFA45-D6F5-436B-8DFA-A675D1FECD5D}.Release|x86.Build.0 = Release|Win32
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+       GlobalSection(ExtensibilityGlobals) = postSolution
+               SolutionGuid = {1B93F7EF-80DC-4175-9456-C28068EF4D6D}
+       EndGlobalSection
+EndGlobal