diff --git a/examples/Animometer.cpp b/examples/Animometer.cpp index 71c5ef82cf..7a27f344be 100644 --- a/examples/Animometer.cpp +++ b/examples/Animometer.cpp @@ -23,9 +23,10 @@ nxt::Device device; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::RenderPipeline pipeline; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; float RandomFloat(float min, float max) { float zeroOne = rand() / float(RAND_MAX); @@ -47,6 +48,8 @@ void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); nxt::ShaderModule vsModule = utils::CreateShaderModule(device, nxt::ShaderStage::Vertex, R"( #version 450 @@ -105,7 +108,9 @@ void init() { })" ); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); + pipeline = device.CreateRenderPipelineBuilder() .SetSubpass(renderpass, 0) .SetStage(nxt::ShaderStage::Vertex, vsModule, "main") @@ -124,6 +129,10 @@ void init() { } void frame() { + nxt::Texture backbuffer; + nxt::Framebuffer framebuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &framebuffer); + static int f = 0; f++; @@ -152,7 +161,9 @@ void frame() { } queue.Submit(50, commands.data()); - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); fprintf(stderr, "frame %i\n", f); } diff --git a/examples/CHelloTriangle.cpp b/examples/CHelloTriangle.cpp index 5eb74d2018..637f5d0496 100644 --- a/examples/CHelloTriangle.cpp +++ b/examples/CHelloTriangle.cpp @@ -19,9 +19,9 @@ nxtDevice device; nxtQueue queue; +nxtSwapChain swapchain; nxtRenderPipeline pipeline; nxtRenderPass renderpass; -nxtFramebuffer framebuffer; void init() { device = CreateCppNXTDevice().Release(); @@ -32,6 +32,15 @@ void init() { nxtQueueBuilderRelease(builder); } + { + nxtSwapChainBuilder builder = nxtDeviceCreateSwapChainBuilder(device); + uint64_t swapchainImpl = GetSwapChainImplementation(); + nxtSwapChainBuilderSetImplementation(builder, swapchainImpl); + swapchain = nxtSwapChainBuilderGetResult(builder); + nxtSwapChainBuilderRelease(builder); + } + nxtSwapChainConfigure(swapchain, NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM, 640, 480); + const char* vs = "#version 450\n" "const vec2 pos[3] = vec2[3](vec2(0.0f, 0.5f), vec2(-0.5f, -0.5f), vec2(0.5f, -0.5f));\n" @@ -57,13 +66,6 @@ void init() { renderpass = nxtRenderPassBuilderGetResult(builder); nxtRenderPassBuilderRelease(builder); } - { - nxtFramebufferBuilder builder = nxtDeviceCreateFramebufferBuilder(device); - nxtFramebufferBuilderSetRenderPass(builder, renderpass); - nxtFramebufferBuilderSetDimensions(builder, 640, 480); - framebuffer = nxtFramebufferBuilderGetResult(builder); - nxtFramebufferBuilderRelease(builder); - } { nxtRenderPipelineBuilder builder = nxtDeviceCreateRenderPipelineBuilder(device); nxtRenderPipelineBuilderSetSubpass(builder, renderpass, 0); @@ -78,6 +80,22 @@ void init() { } void frame() { + nxtTexture backbuffer = nxtSwapChainGetNextTexture(swapchain); + nxtTextureView backbufferView; + { + nxtTextureViewBuilder builder = nxtTextureCreateTextureViewBuilder(backbuffer); + backbufferView = nxtTextureViewBuilderGetResult(builder); + nxtTextureViewBuilderRelease(builder); + } + nxtFramebuffer framebuffer; + { + nxtFramebufferBuilder builder = nxtDeviceCreateFramebufferBuilder(device); + nxtFramebufferBuilderSetRenderPass(builder, renderpass); + nxtFramebufferBuilderSetDimensions(builder, 640, 480); + nxtFramebufferBuilderSetAttachment(builder, 0, backbufferView); + framebuffer = nxtFramebufferBuilderGetResult(builder); + nxtFramebufferBuilderRelease(builder); + } nxtCommandBuffer commands; { nxtCommandBufferBuilder builder = nxtDeviceCreateCommandBufferBuilder(device); @@ -93,8 +111,12 @@ void frame() { nxtQueueSubmit(queue, 1, &commands); nxtCommandBufferRelease(commands); + nxtTextureTransitionUsage(backbuffer, NXT_TEXTURE_USAGE_BIT_PRESENT); + nxtSwapChainPresent(swapchain, backbuffer); + nxtFramebufferRelease(framebuffer); + nxtTextureViewRelease(backbufferView); - DoSwapBuffers(); + DoFlush(); } int main(int argc, const char* argv[]) { diff --git a/examples/ComputeBoids.cpp b/examples/ComputeBoids.cpp index c21ff2643b..9e559e989c 100644 --- a/examples/ComputeBoids.cpp +++ b/examples/ComputeBoids.cpp @@ -25,20 +25,19 @@ nxt::Device device; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::Buffer modelBuffer; std::array particleBuffers; nxt::RenderPipeline renderPipeline; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; nxt::Buffer updateParams; nxt::ComputePipeline updatePipeline; std::array updateBGs; -std::array commandBuffers; - size_t pingpong = 0; static const uint32_t kNumParticles = 1000; @@ -124,7 +123,9 @@ void initRender() { .SetInput(1, sizeof(glm::vec2), nxt::InputStepMode::Vertex) .GetResult(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); + renderPipeline = device.CreateRenderPipelineBuilder() .SetSubpass(renderpass, 0) .SetStage(nxt::ShaderStage::Vertex, vsModule, "main") @@ -259,48 +260,54 @@ void initSim() { } } -void initCommandBuffers() { +nxt::CommandBuffer createCommandBuffer(const nxt::Framebuffer& framebuffer, size_t i) { static const uint32_t zeroOffsets[1] = {0}; - for (size_t i = 0; i < 2; ++i) { - auto& bufferSrc = particleBuffers[i]; - auto& bufferDst = particleBuffers[(i + 1) % 2]; - commandBuffers[i] = device.CreateCommandBufferBuilder() - .BeginComputePass() - .SetComputePipeline(updatePipeline) - .TransitionBufferUsage(bufferSrc, nxt::BufferUsageBit::Storage) - .TransitionBufferUsage(bufferDst, nxt::BufferUsageBit::Storage) - .SetBindGroup(0, updateBGs[i]) - .Dispatch(kNumParticles, 1, 1) - .EndComputePass() + auto& bufferSrc = particleBuffers[i]; + auto& bufferDst = particleBuffers[(i + 1) % 2]; + return device.CreateCommandBufferBuilder() + .BeginComputePass() + .SetComputePipeline(updatePipeline) + .TransitionBufferUsage(bufferSrc, nxt::BufferUsageBit::Storage) + .TransitionBufferUsage(bufferDst, nxt::BufferUsageBit::Storage) + .SetBindGroup(0, updateBGs[i]) + .Dispatch(kNumParticles, 1, 1) + .EndComputePass() - .BeginRenderPass(renderpass, framebuffer) - .BeginRenderSubpass() - .SetRenderPipeline(renderPipeline) - .TransitionBufferUsage(bufferDst, nxt::BufferUsageBit::Vertex) - .SetVertexBuffers(0, 1, &bufferDst, zeroOffsets) - .SetVertexBuffers(1, 1, &modelBuffer, zeroOffsets) - .DrawArrays(3, kNumParticles, 0, 0) - .EndRenderSubpass() - .EndRenderPass() + .BeginRenderPass(renderpass, framebuffer) + .BeginRenderSubpass() + .SetRenderPipeline(renderPipeline) + .TransitionBufferUsage(bufferDst, nxt::BufferUsageBit::Vertex) + .SetVertexBuffers(0, 1, &bufferDst, zeroOffsets) + .SetVertexBuffers(1, 1, &modelBuffer, zeroOffsets) + .DrawArrays(3, kNumParticles, 0, 0) + .EndRenderSubpass() + .EndRenderPass() - .GetResult(); - } + .GetResult(); } void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); initBuffers(); initRender(); initSim(); - initCommandBuffers(); } void frame() { - queue.Submit(1, &commandBuffers[pingpong]); - DoSwapBuffers(); + nxt::Texture backbuffer; + nxt::Framebuffer framebuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &framebuffer); + + nxt::CommandBuffer commandBuffer = createCommandBuffer(framebuffer, pingpong); + queue.Submit(1, &commandBuffer); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); pingpong = (pingpong + 1) % 2; } diff --git a/examples/HelloCompute.cpp b/examples/HelloCompute.cpp index 1ee27e24d4..2fbf3993f4 100644 --- a/examples/HelloCompute.cpp +++ b/examples/HelloCompute.cpp @@ -21,11 +21,12 @@ nxt::Device device; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::Buffer buffer; nxt::RenderPipeline renderPipeline; nxt::BindGroup renderBindGroup; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; nxt::ComputePipeline computePipeline; nxt::BindGroup computeBindGroup; @@ -33,6 +34,8 @@ void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); struct {uint32_t a; float b;} s; memset(&s, 0, sizeof(s)); @@ -109,7 +112,9 @@ void init() { .SetBindGroupLayout(0, bgl) .GetResult(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); + renderPipeline = device.CreateRenderPipelineBuilder() .SetSubpass(renderpass, 0) .SetLayout(pl) @@ -126,6 +131,10 @@ void init() { } void frame() { + nxt::Texture backbuffer; + nxt::Framebuffer framebuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &framebuffer); + nxt::CommandBuffer commands = device.CreateCommandBufferBuilder() .BeginComputePass() .SetComputePipeline(computePipeline) @@ -146,7 +155,9 @@ void frame() { .GetResult(); queue.Submit(1, &commands); - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); } int main(int argc, const char* argv[]) { diff --git a/examples/HelloDepthStencil.cpp b/examples/HelloDepthStencil.cpp index df8682b5d8..b8e9f24926 100644 --- a/examples/HelloDepthStencil.cpp +++ b/examples/HelloDepthStencil.cpp @@ -35,11 +35,12 @@ nxt::BindGroup bindGroup[2]; nxt::BindGroup cubeTransformBindGroup[2]; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::RenderPipeline pipeline; nxt::RenderPipeline planePipeline; nxt::RenderPipeline reflectionPipeline; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; void initBuffers() { static const uint32_t indexData[6*6] = { @@ -114,6 +115,8 @@ void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); initBuffers(); @@ -206,7 +209,8 @@ void init() { .SetBufferViews(1, 1, &transformBufferView[1]) .GetResult(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); auto depthStencilState = device.CreateDepthStencilStateBuilder() .SetDepthCompareFunction(nxt::CompareFunction::Less) @@ -271,6 +275,10 @@ void frame() { cameraBuffer.TransitionUsage(nxt::BufferUsageBit::TransferDst); cameraBuffer.SetSubData(0, sizeof(CameraData) / sizeof(uint32_t), reinterpret_cast(&cameraData)); + nxt::Texture backbuffer; + nxt::Framebuffer framebuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &framebuffer); + nxt::CommandBuffer commands = device.CreateCommandBufferBuilder() .BeginRenderPass(renderpass, framebuffer) .BeginRenderSubpass() @@ -296,7 +304,9 @@ void frame() { .GetResult(); queue.Submit(1, &commands); - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); } int main(int argc, const char* argv[]) { diff --git a/examples/HelloIndices.cpp b/examples/HelloIndices.cpp index 7f85ab769e..dcd75e1fa3 100644 --- a/examples/HelloIndices.cpp +++ b/examples/HelloIndices.cpp @@ -25,9 +25,10 @@ nxt::Buffer indexBuffer; nxt::Buffer vertexBuffer; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::RenderPipeline pipeline; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; void initBuffers() { static const uint32_t indexData[3] = { @@ -47,6 +48,8 @@ void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); initBuffers(); @@ -71,7 +74,9 @@ void init() { .SetInput(0, 4 * sizeof(float), nxt::InputStepMode::Vertex) .GetResult(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); + pipeline = device.CreateRenderPipelineBuilder() .SetSubpass(renderpass, 0) .SetStage(nxt::ShaderStage::Vertex, vsModule, "main") @@ -81,6 +86,10 @@ void init() { } void frame() { + nxt::Texture backbuffer; + nxt::Framebuffer framebuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &framebuffer); + static const uint32_t vertexBufferOffsets[1] = {0}; nxt::CommandBuffer commands = device.CreateCommandBufferBuilder() .BeginRenderPass(renderpass, framebuffer) @@ -94,7 +103,9 @@ void frame() { .GetResult(); queue.Submit(1, &commands); - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); } int main(int argc, const char* argv[]) { diff --git a/examples/HelloInstancing.cpp b/examples/HelloInstancing.cpp index 0c431bea1a..19ae47d7cb 100644 --- a/examples/HelloInstancing.cpp +++ b/examples/HelloInstancing.cpp @@ -25,9 +25,10 @@ nxt::Buffer vertexBuffer; nxt::Buffer instanceBuffer; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::RenderPipeline pipeline; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; void initBuffers() { static const float vertexData[12] = { @@ -50,6 +51,8 @@ void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); initBuffers(); @@ -77,7 +80,9 @@ void init() { .SetInput(1, 2 * sizeof(float), nxt::InputStepMode::Instance) .GetResult(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); + pipeline = device.CreateRenderPipelineBuilder() .SetSubpass(renderpass, 0) .SetStage(nxt::ShaderStage::Vertex, vsModule, "main") @@ -87,6 +92,10 @@ void init() { } void frame() { + nxt::Texture backbuffer; + nxt::Framebuffer framebuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &framebuffer); + static const uint32_t vertexBufferOffsets[1] = {0}; nxt::CommandBuffer commands = device.CreateCommandBufferBuilder() .BeginRenderPass(renderpass, framebuffer) @@ -100,7 +109,9 @@ void frame() { .GetResult(); queue.Submit(1, &commands); - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); } int main(int argc, const char* argv[]) { diff --git a/examples/HelloTriangle.cpp b/examples/HelloTriangle.cpp index d98bfb8d45..8d491f7637 100644 --- a/examples/HelloTriangle.cpp +++ b/examples/HelloTriangle.cpp @@ -28,9 +28,10 @@ nxt::Texture texture; nxt::Sampler sampler; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::RenderPipeline pipeline; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; nxt::BindGroup bindGroup; void initBuffers() { @@ -81,6 +82,8 @@ void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); initBuffers(); initTextures(); @@ -118,7 +121,9 @@ void init() { .SetBindGroupLayout(0, bgl) .GetResult(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); + pipeline = device.CreateRenderPipelineBuilder() .SetSubpass(renderpass, 0) .SetLayout(pl) @@ -142,6 +147,11 @@ void frame() { s.a = (s.a + 1) % 256; s.b += 0.02f; if (s.b >= 1.0f) {s.b = 0.0f;} + + nxt::Texture backbuffer; + nxt::Framebuffer framebuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &framebuffer); + static const uint32_t vertexBufferOffsets[1] = {0}; nxt::CommandBuffer commands = device.CreateCommandBufferBuilder() .BeginRenderPass(renderpass, framebuffer) @@ -156,7 +166,9 @@ void frame() { .GetResult(); queue.Submit(1, &commands); - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); } int main(int argc, const char* argv[]) { diff --git a/examples/HelloUBO.cpp b/examples/HelloUBO.cpp index 3a59c4a242..bfa4c54b62 100644 --- a/examples/HelloUBO.cpp +++ b/examples/HelloUBO.cpp @@ -19,9 +19,10 @@ nxt::Device device; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::RenderPipeline pipeline; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; nxt::Buffer buffer; nxt::BindGroup bindGroup; @@ -31,6 +32,8 @@ void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); nxt::ShaderModule vsModule = utils::CreateShaderModule(device, nxt::ShaderStage::Vertex, R"( #version 450 @@ -60,7 +63,9 @@ void init() { .SetBindGroupLayout(0, bgl) .GetResult(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); + pipeline = device.CreateRenderPipelineBuilder() .SetSubpass(renderpass, 0) .SetLayout(pl) @@ -93,6 +98,10 @@ void frame() { buffer.TransitionUsage(nxt::BufferUsageBit::TransferDst); buffer.SetSubData(0, sizeof(s) / sizeof(uint32_t), reinterpret_cast(&s)); + nxt::Texture backbuffer; + nxt::Framebuffer framebuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &framebuffer); + nxt::CommandBuffer commands = device.CreateCommandBufferBuilder() .BeginRenderPass(renderpass, framebuffer) .BeginRenderSubpass() @@ -105,7 +114,9 @@ void frame() { .GetResult(); queue.Submit(1, &commands); - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); } int main(int argc, const char* argv[]) { diff --git a/examples/HelloVertices.cpp b/examples/HelloVertices.cpp index 6f6c2e21c5..129627b8c7 100644 --- a/examples/HelloVertices.cpp +++ b/examples/HelloVertices.cpp @@ -24,9 +24,10 @@ nxt::Device device; nxt::Buffer vertexBuffer; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::RenderPipeline pipeline; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; void initBuffers() { static const float vertexData[12] = { @@ -41,6 +42,8 @@ void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); initBuffers(); @@ -65,7 +68,9 @@ void init() { .SetInput(0, 4 * sizeof(float), nxt::InputStepMode::Vertex) .GetResult(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); + pipeline = device.CreateRenderPipelineBuilder() .SetSubpass(renderpass, 0) .SetStage(nxt::ShaderStage::Vertex, vsModule, "main") @@ -75,6 +80,10 @@ void init() { } void frame() { + nxt::Texture backbuffer; + nxt::Framebuffer framebuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &framebuffer); + static const uint32_t vertexBufferOffsets[1] = {0}; nxt::CommandBuffer commands = device.CreateCommandBufferBuilder() .BeginRenderPass(renderpass, framebuffer) @@ -87,7 +96,9 @@ void frame() { .GetResult(); queue.Submit(1, &commands); - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); } int main(int argc, const char* argv[]) { diff --git a/examples/RenderToTexture.cpp b/examples/RenderToTexture.cpp index 65fa858817..93e220070d 100644 --- a/examples/RenderToTexture.cpp +++ b/examples/RenderToTexture.cpp @@ -29,11 +29,11 @@ nxt::TextureView renderTargetView; nxt::Sampler samplerPost; nxt::Queue queue; +nxt::SwapChain swapchain; nxt::RenderPipeline pipeline; nxt::RenderPipeline pipelinePost; nxt::BindGroup bindGroup; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; void initBuffers() { static const float vertexData[12] = { @@ -81,14 +81,6 @@ void initRenderPass() { // subpass 1 .SubpassSetColorAttachment(1, 0, 1) // -> back buffer .GetResult(); - framebuffer = device.CreateFramebufferBuilder() - .SetRenderPass(renderpass) - // attachment 0 -> render target - .SetAttachment(0, renderTargetView) - // attachment 1 -> back buffer - // (implicit) // TODO(kainino@chromium.org): use the texture provided by WSI - .SetDimensions(640, 480) - .GetResult(); } void initPipeline() { @@ -174,6 +166,8 @@ void init() { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); initBuffers(); initTextures(); @@ -183,6 +177,15 @@ void init() { } void frame() { + nxt::Texture backbuffer = swapchain.GetNextTexture(); + auto backbufferView = backbuffer.CreateTextureViewBuilder().GetResult(); + auto framebuffer = device.CreateFramebufferBuilder() + .SetRenderPass(renderpass) + .SetDimensions(640, 480) + .SetAttachment(0, renderTargetView) + .SetAttachment(1, backbufferView) + .GetResult(); + static const uint32_t vertexBufferOffsets[1] = {0}; nxt::CommandBuffer commands = device.CreateCommandBufferBuilder() .BeginRenderPass(renderpass, framebuffer) @@ -204,7 +207,9 @@ void frame() { .GetResult(); queue.Submit(1, &commands); - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); } int main(int argc, const char* argv[]) { diff --git a/examples/SampleUtils.cpp b/examples/SampleUtils.cpp index d87d8e23da..b666d3fc0e 100644 --- a/examples/SampleUtils.cpp +++ b/examples/SampleUtils.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "GLFW/glfw3.h" #include @@ -39,9 +40,9 @@ enum class CmdBufType { static utils::BackendType backendType = utils::BackendType::D3D12; #elif defined(NXT_ENABLE_BACKEND_METAL) static utils::BackendType backendType = utils::BackendType::Metal; -#elif defined(NXT_ENABLE_BACKEND_VULKAN) - static utils::BackendType backendType = utils::BackendType::OpenGL; #elif defined(NXT_ENABLE_BACKEND_OPENGL) + static utils::BackendType backendType = utils::BackendType::OpenGL; +#elif defined(NXT_ENABLE_BACKEND_VULKAN) static utils::BackendType backendType = utils::BackendType::Vulkan; #else #error @@ -111,6 +112,56 @@ nxt::Device CreateCppNXTDevice() { return nxt::Device::Acquire(cDevice); } +uint64_t GetSwapChainImplementation() { + return binding->GetSwapChainImplementation(); +} + +nxt::SwapChain GetSwapChain(const nxt::Device &device) { + return device.CreateSwapChainBuilder() + .SetImplementation(GetSwapChainImplementation()) + .GetResult(); +} + +nxt::RenderPass CreateDefaultRenderPass(const nxt::Device& device) { + return device.CreateRenderPassBuilder() + .SetAttachmentCount(2) + .AttachmentSetFormat(0, nxt::TextureFormat::R8G8B8A8Unorm) + .AttachmentSetFormat(1, nxt::TextureFormat::D32FloatS8Uint) + .SetSubpassCount(1) + .SubpassSetColorAttachment(0, 0, 0) + .SubpassSetDepthStencilAttachment(0, 1) + .GetResult(); +} + +nxt::TextureView CreateDefaultDepthStencilView(const nxt::Device& device) { + auto depthStencilTexture = device.CreateTextureBuilder() + .SetDimension(nxt::TextureDimension::e2D) + .SetExtent(640, 480, 1) + .SetFormat(nxt::TextureFormat::D32FloatS8Uint) + .SetMipLevels(1) + .SetAllowedUsage(nxt::TextureUsageBit::OutputAttachment) + .GetResult(); + depthStencilTexture.FreezeUsage(nxt::TextureUsageBit::OutputAttachment); + return depthStencilTexture.CreateTextureViewBuilder() + .GetResult(); +} + +void GetNextFramebuffer(const nxt::Device& device, + const nxt::RenderPass& renderpass, + const nxt::SwapChain& swapchain, + const nxt::TextureView& depthStencilView, + nxt::Texture* backbuffer, + nxt::Framebuffer* framebuffer) { + *backbuffer = swapchain.GetNextTexture(); + auto backbufferView = backbuffer->CreateTextureViewBuilder().GetResult(); + *framebuffer = device.CreateFramebufferBuilder() + .SetRenderPass(renderpass) + .SetDimensions(640, 480) + .SetAttachment(0, backbufferView) + .SetAttachment(1, depthStencilView) + .GetResult(); +} + bool InitSample(int argc, const char** argv) { for (int i = 0; i < argc; i++) { if (std::string("-b") == argv[i] || std::string("--backend") == argv[i]) { @@ -138,7 +189,7 @@ bool InitSample(int argc, const char** argv) { fprintf(stderr, "--backend expects a backend name (opengl, metal, d3d12, null, vulkan)\n"); return false; } - if (std::string("-c") == argv[i] || std::string("--comand-buffer") == argv[i]) { + if (std::string("-c") == argv[i] || std::string("--command-buffer") == argv[i]) { i++; if (i < argc && std::string("none") == argv[i]) { cmdBufType = CmdBufType::None; @@ -161,13 +212,12 @@ bool InitSample(int argc, const char** argv) { return true; } -void DoSwapBuffers() { +void DoFlush() { if (cmdBufType == CmdBufType::Terrible) { c2sBuf->Flush(); s2cBuf->Flush(); } glfwPollEvents(); - binding->SwapBuffers(); } bool ShouldQuit() { diff --git a/examples/SampleUtils.h b/examples/SampleUtils.h index 54e5f7cd32..22bf5c883a 100644 --- a/examples/SampleUtils.h +++ b/examples/SampleUtils.h @@ -13,12 +13,23 @@ // limitations under the License. #include +#include bool InitSample(int argc, const char** argv); -void DoSwapBuffers(); +void DoFlush(); bool ShouldQuit(); struct GLFWwindow; struct GLFWwindow* GetGLFWWindow(); nxt::Device CreateCppNXTDevice(); +uint64_t GetSwapChainImplementation(); +nxt::SwapChain GetSwapChain(const nxt::Device& device); +nxt::RenderPass CreateDefaultRenderPass(const nxt::Device& device); +nxt::TextureView CreateDefaultDepthStencilView(const nxt::Device& device); +void GetNextFramebuffer(const nxt::Device& device, + const nxt::RenderPass& renderPass, + const nxt::SwapChain& swapchain, + const nxt::TextureView& depthStencilView, + nxt::Texture* backbuffer, + nxt::Framebuffer* framebuffer); diff --git a/examples/glTFViewer/glTFViewer.cpp b/examples/glTFViewer/glTFViewer.cpp index 74720e423d..2cdc07aa2d 100644 --- a/examples/glTFViewer/glTFViewer.cpp +++ b/examples/glTFViewer/glTFViewer.cpp @@ -77,8 +77,10 @@ struct u_transform_block { nxt::Device device; nxt::Queue queue; +nxt::SwapChain swapchain; +nxt::TextureView depthStencilView; nxt::RenderPass renderpass; -nxt::Framebuffer framebuffer; +nxt::Framebuffer lastFramebuffer; nxt::Buffer defaultBuffer; std::map buffers; @@ -467,7 +469,11 @@ namespace { device = CreateCppNXTDevice(); queue = device.CreateQueueBuilder().GetResult(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + swapchain = GetSwapChain(device); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + + renderpass = CreateDefaultRenderPass(device); + depthStencilView = CreateDefaultDepthStencilView(device); initBuffers(); initSamplers(); @@ -506,7 +512,7 @@ namespace { material.uniformBuffer.SetSubData(0, sizeof(u_transform_block) / sizeof(uint32_t), reinterpret_cast(&transforms)); - cmd.BeginRenderPass(renderpass, framebuffer); + cmd.BeginRenderPass(renderpass, lastFramebuffer); cmd.BeginRenderSubpass(); cmd.SetRenderPipeline(material.pipeline); cmd.TransitionBufferUsage(material.uniformBuffer, nxt::BufferUsageBit::Uniform); @@ -586,12 +592,17 @@ namespace { } void frame() { + nxt::Texture backbuffer; + GetNextFramebuffer(device, renderpass, swapchain, depthStencilView, &backbuffer, &lastFramebuffer); + const auto& defaultSceneNodes = scene.scenes.at(scene.defaultScene); for (const auto& n : defaultSceneNodes) { const auto& node = scene.nodes.at(n); drawNode(node); } - DoSwapBuffers(); + backbuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backbuffer); + DoFlush(); } } diff --git a/next.json b/next.json index 05fe50e123..27707ced82 100644 --- a/next.json +++ b/next.json @@ -521,6 +521,10 @@ "name": "create shader module builder", "returns": "shader module builder" }, + { + "name": "create swap chain builder", + "returns": "swap chain builder" + }, { "name": "create texture builder", "returns": "texture builder" diff --git a/src/backend/Device.cpp b/src/backend/Device.cpp index 404b7c8149..3e802604c0 100644 --- a/src/backend/Device.cpp +++ b/src/backend/Device.cpp @@ -28,6 +28,7 @@ #include "backend/RenderPipeline.h" #include "backend/Sampler.h" #include "backend/ShaderModule.h" +#include "backend/SwapChain.h" #include "backend/Texture.h" #include @@ -130,6 +131,9 @@ namespace backend { ShaderModuleBuilder* DeviceBase::CreateShaderModuleBuilder() { return new ShaderModuleBuilder(this); } + SwapChainBuilder* DeviceBase::CreateSwapChainBuilder() { + return new SwapChainBuilder(this); + } TextureBuilder* DeviceBase::CreateTextureBuilder() { return new TextureBuilder(this); } diff --git a/src/backend/SwapChain.cpp b/src/backend/SwapChain.cpp index c4e41d409c..3ded8af929 100644 --- a/src/backend/SwapChain.cpp +++ b/src/backend/SwapChain.cpp @@ -26,6 +26,8 @@ namespace backend { } SwapChainBase::~SwapChainBase() { + const auto& im = GetImplementation(); + im.Destroy(im.userData); } DeviceBase* SwapChainBase::GetDevice() { @@ -41,6 +43,8 @@ namespace backend { this->format = format; this->width = width; this->height = height; + implementation.Configure(implementation.userData, + static_cast(format), width, height); } TextureBase* SwapChainBase::GetNextTexture() { @@ -102,7 +106,7 @@ namespace backend { nxtSwapChainImplementation& impl = *reinterpret_cast(implementation); - if (!impl.Init || impl.Destroy || !impl.Configure || + if (!impl.Init || !impl.Destroy || !impl.Configure || !impl.GetNextTexture || !impl.Present) { HandleError("Implementation is incomplete"); return; diff --git a/src/backend/d3d12/BindGroupD3D12.cpp b/src/backend/d3d12/BindGroupD3D12.cpp index a5d16c9272..4367cc5c12 100644 --- a/src/backend/d3d12/BindGroupD3D12.cpp +++ b/src/backend/d3d12/BindGroupD3D12.cpp @@ -61,7 +61,7 @@ namespace d3d12 { { auto* view = ToBackend(GetBindingAsTextureView(binding)); auto& srv = view->GetSRVDescriptor(); - d3d12Device->CreateShaderResourceView(ToBackend(view->GetTexture())->GetD3D12Resource().Get(), &srv, cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset + bindingOffsets[binding])); + d3d12Device->CreateShaderResourceView(ToBackend(view->GetTexture())->GetD3D12Resource(), &srv, cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset + bindingOffsets[binding])); } break; case nxt::BindingType::Sampler: diff --git a/src/backend/d3d12/CommandBufferD3D12.cpp b/src/backend/d3d12/CommandBufferD3D12.cpp index c0dac98f34..86e458bf16 100644 --- a/src/backend/d3d12/CommandBufferD3D12.cpp +++ b/src/backend/d3d12/CommandBufferD3D12.cpp @@ -309,7 +309,7 @@ namespace d3d12 { ); D3D12_TEXTURE_COPY_LOCATION textureLocation; - textureLocation.pResource = texture->GetD3D12Resource().Get(); + textureLocation.pResource = texture->GetD3D12Resource(); textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; textureLocation.SubresourceIndex = copy->destination.level; @@ -358,7 +358,7 @@ namespace d3d12 { ); D3D12_TEXTURE_COPY_LOCATION textureLocation; - textureLocation.pResource = texture->GetD3D12Resource().Get(); + textureLocation.pResource = texture->GetD3D12Resource(); textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; textureLocation.SubresourceIndex = copy->source.level; diff --git a/src/backend/d3d12/D3D12Backend.cpp b/src/backend/d3d12/D3D12Backend.cpp index 357bc93cec..04b45fa85b 100644 --- a/src/backend/d3d12/D3D12Backend.cpp +++ b/src/backend/d3d12/D3D12Backend.cpp @@ -51,11 +51,6 @@ namespace d3d12 { return backendDevice->GetCommandQueue(); } - void SetNextTexture(nxtDevice device, ComPtr resource) { - Device* backendDevice = reinterpret_cast(device); - backendDevice->SetNextTexture(resource); - } - uint64_t GetSerial(const nxtDevice device) { const Device* backendDevice = reinterpret_cast(device); return backendDevice->GetSerial(); @@ -101,6 +96,8 @@ namespace d3d12 { ASSERT_SUCCESS(d3d12Device->CreateFence(serial, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence))); fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); ASSERT(fenceEvent != nullptr); + + NextSerial(); } Device::~Device() { @@ -163,15 +160,6 @@ namespace d3d12 { return pendingCommands.commandList; } - ComPtr Device::GetCurrentTexture() { - return nextTexture; - } - - void Device::SetNextTexture(ComPtr resource) { - nextTexture = resource; - } - - void Device::TickImpl() { // Perform cleanup operations to free unused objects const uint64_t lastCompletedSerial = fence->GetCompletedValue(); diff --git a/src/backend/d3d12/D3D12Backend.h b/src/backend/d3d12/D3D12Backend.h index 4fc63eae61..fab0af7153 100644 --- a/src/backend/d3d12/D3D12Backend.h +++ b/src/backend/d3d12/D3D12Backend.h @@ -120,9 +120,6 @@ namespace d3d12 { void OpenCommandList(ComPtr* commandList); ComPtr GetPendingCommandList(); - ComPtr GetCurrentTexture(); - void SetNextTexture(ComPtr resource); - uint64_t GetSerial() const; void NextSerial(); void WaitForSerial(uint64_t serial); @@ -147,8 +144,6 @@ namespace d3d12 { ComPtr commandList; bool open = false; } pendingCommands; - - ComPtr nextTexture; }; class DepthStencilState : public DepthStencilStateBase { diff --git a/src/backend/d3d12/FramebufferD3D12.cpp b/src/backend/d3d12/FramebufferD3D12.cpp index ea1ae4f732..214b5a4313 100644 --- a/src/backend/d3d12/FramebufferD3D12.cpp +++ b/src/backend/d3d12/FramebufferD3D12.cpp @@ -78,20 +78,7 @@ namespace d3d12 { for (uint32_t index : IterateBitSet(subpassInfo.colorAttachmentsSet)) { uint32_t heapIndex = attachmentHeapIndices[subpassInfo.colorAttachments[index]]; - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetCPUHandle(heapIndex); - - uint32_t attachment = subpassInfo.colorAttachments[index]; - if (!GetTextureView(attachment)) { - // TODO(kainino@chromium.org): null=backbuffer hack - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; - rtvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - rtvDesc.Texture2D.MipSlice = 0; - rtvDesc.Texture2D.PlaneSlice = 0; - device->GetD3D12Device()->CreateRenderTargetView(device->GetCurrentTexture().Get(), &rtvDesc, rtvHandle); - } - - args.RTVs[args.numRTVs++] = rtvHandle; + args.RTVs[args.numRTVs++] = rtvHeap.GetCPUHandle(heapIndex); } if (subpassInfo.depthStencilAttachmentSet) { uint32_t heapIndex = attachmentHeapIndices[subpassInfo.depthStencilAttachment]; diff --git a/src/backend/d3d12/SwapChainD3D12.cpp b/src/backend/d3d12/SwapChainD3D12.cpp index 61d693253d..4d3d87425d 100644 --- a/src/backend/d3d12/SwapChainD3D12.cpp +++ b/src/backend/d3d12/SwapChainD3D12.cpp @@ -14,6 +14,7 @@ #include "backend/d3d12/SwapChainD3D12.h" +#include "backend/d3d12/D3D12Backend.h" #include "backend/d3d12/TextureD3D12.h" #include @@ -25,19 +26,23 @@ namespace d3d12 { : SwapChainBase(builder) { const auto& im = GetImplementation(); nxtWSIContextD3D12 wsiContext = {}; - // TODO(kainino@chromium.org): set up wsiContext + wsiContext.device = reinterpret_cast(GetDevice()); im.Init(im.userData, &wsiContext); - - // TODO(kainino@chromium.org): set up D3D12 swapchain } SwapChain::~SwapChain() { - // TODO(kainino@chromium.org): clean up D3D12 swapchain } TextureBase* SwapChain::GetNextTextureImpl(TextureBuilder* builder) { - ComPtr nativeTexture = nullptr; - // TODO(kainino@chromium.org): obtain native texture from D3D12 swapchain + const auto& im = GetImplementation(); + nxtSwapChainNextTexture next = {}; + nxtSwapChainError error = im.GetNextTexture(im.userData, &next); + if (error) { + GetDevice()->HandleError(error); + return nullptr; + } + + ID3D12Resource* nativeTexture = reinterpret_cast(next.texture); return new Texture(builder, nativeTexture); } diff --git a/src/backend/d3d12/TextureD3D12.cpp b/src/backend/d3d12/TextureD3D12.cpp index 0148182d45..d74cb167f8 100644 --- a/src/backend/d3d12/TextureD3D12.cpp +++ b/src/backend/d3d12/TextureD3D12.cpp @@ -105,23 +105,28 @@ namespace d3d12 { resourceDescriptor.Flags = D3D12ResourceFlags(GetAllowedUsage(), GetFormat()); resource = device->GetResourceAllocator()->Allocate(D3D12_HEAP_TYPE_DEFAULT, resourceDescriptor, D3D12TextureUsage(GetUsage(), GetFormat())); + resourcePtr = resource.Get(); } - Texture::Texture(TextureBuilder* builder, ComPtr nativeTexture) - : TextureBase(builder), device(ToBackend(builder->GetDevice())), resource(nativeTexture) { + // With this constructor, the lifetime of the ID3D12Resource is externally managed. + Texture::Texture(TextureBuilder* builder, ID3D12Resource* nativeTexture) + : TextureBase(builder), device(ToBackend(builder->GetDevice())), + resourcePtr(nativeTexture) { } Texture::~Texture() { - // TODO(kainino@chromium.org): Maybe don't release when using the native texture constructor? - device->GetResourceAllocator()->Release(resource); + if (resource) { + // If we own the resource, release it. + device->GetResourceAllocator()->Release(resource); + } } DXGI_FORMAT Texture::GetD3D12Format() const { return D3D12TextureFormat(GetFormat()); } - ComPtr Texture::GetD3D12Resource() { - return resource; + ID3D12Resource* Texture::GetD3D12Resource() { + return resourcePtr; } bool Texture::GetResourceTransitionBarrier(nxt::TextureUsageBit currentUsage, nxt::TextureUsageBit targetUsage, D3D12_RESOURCE_BARRIER* barrier) { @@ -134,7 +139,7 @@ namespace d3d12 { barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier->Transition.pResource = resource.Get(); + barrier->Transition.pResource = resourcePtr; barrier->Transition.StateBefore = stateBefore; barrier->Transition.StateAfter = stateAfter; barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; diff --git a/src/backend/d3d12/TextureD3D12.h b/src/backend/d3d12/TextureD3D12.h index 95c5a37945..53ca0bc0ec 100644 --- a/src/backend/d3d12/TextureD3D12.h +++ b/src/backend/d3d12/TextureD3D12.h @@ -29,16 +29,17 @@ namespace d3d12 { class Texture : public TextureBase { public: Texture(TextureBuilder* builder); - Texture(TextureBuilder* builder, ComPtr nativeTexture); + Texture(TextureBuilder* builder, ID3D12Resource* nativeTexture); ~Texture(); DXGI_FORMAT GetD3D12Format() const; - ComPtr GetD3D12Resource(); + ID3D12Resource* GetD3D12Resource(); bool GetResourceTransitionBarrier(nxt::TextureUsageBit currentUsage, nxt::TextureUsageBit targetUsage, D3D12_RESOURCE_BARRIER* barrier); private: Device* device; - ComPtr resource; + ComPtr resource = {}; + ID3D12Resource* resourcePtr = nullptr; // NXT API void TransitionUsageImpl(nxt::TextureUsageBit currentUsage, nxt::TextureUsageBit targetUsage) override; diff --git a/src/backend/metal/CommandBufferMTL.mm b/src/backend/metal/CommandBufferMTL.mm index 25203cc48b..f282ea8591 100644 --- a/src/backend/metal/CommandBufferMTL.mm +++ b/src/backend/metal/CommandBufferMTL.mm @@ -90,15 +90,8 @@ namespace metal { for (uint32_t index = 0; index < info.colorAttachments.size(); ++index) { uint32_t attachment = info.colorAttachments[index]; - // TODO(kainino@chromium.org): currently a 'null' texture view - // falls back to the 'back buffer' but this should go away - // when we have WSI. - id texture = nil; - if (auto textureView = currentFramebuffer->GetTextureView(attachment)) { - texture = ToBackend(textureView->GetTexture())->GetMTLTexture(); - } else { - texture = device->GetCurrentTexture(); - } + auto textureView = currentFramebuffer->GetTextureView(attachment); + auto texture = ToBackend(textureView->GetTexture())->GetMTLTexture(); descriptor.colorAttachments[index].texture = texture; descriptor.colorAttachments[index].loadAction = MTLLoadActionLoad; descriptor.colorAttachments[index].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 0.0); diff --git a/src/backend/metal/MetalBackend.h b/src/backend/metal/MetalBackend.h index 28ef57d83f..6ec8153cb5 100644 --- a/src/backend/metal/MetalBackend.h +++ b/src/backend/metal/MetalBackend.h @@ -109,11 +109,7 @@ namespace metal { void TickImpl() override; - void SetNextDrawable(id drawable); - void Present(); - id GetMTLDevice(); - id GetCurrentTexture(); id GetPendingCommandBuffer(); void SubmitPendingCommandBuffer(); @@ -130,9 +126,6 @@ namespace metal { MapReadRequestTracker* mapReadTracker; ResourceUploader* resourceUploader; - id currentDrawable = nil; - id currentTexture = nil; - Serial finishedCommandSerial = 0; Serial pendingCommandSerial = 1; id pendingCommands = nil; diff --git a/src/backend/metal/MetalBackend.mm b/src/backend/metal/MetalBackend.mm index 19d35b4b62..c59656d21b 100644 --- a/src/backend/metal/MetalBackend.mm +++ b/src/backend/metal/MetalBackend.mm @@ -41,16 +41,6 @@ namespace metal { *device = reinterpret_cast(new Device(metalDevice)); } - void SetNextDrawable(nxtDevice device, id drawable) { - Device* backendDevice = reinterpret_cast(device); - backendDevice->SetNextDrawable(drawable); - } - - void Present(nxtDevice device) { - Device* backendDevice = reinterpret_cast(device); - backendDevice->Present(); - } - // Device Device::Device(id mtlDevice) @@ -86,9 +76,6 @@ namespace metal { [commandQueue release]; commandQueue = nil; - - [currentTexture release]; - currentTexture = nil; } BindGroupBase* Device::CreateBindGroup(BindGroupBuilder* builder) { @@ -155,43 +142,10 @@ namespace metal { SubmitPendingCommandBuffer(); } - void Device::SetNextDrawable(id drawable) { - [currentDrawable release]; - currentDrawable = drawable; - [currentDrawable retain]; - - [currentTexture release]; - currentTexture = drawable.texture; - [currentTexture retain]; - - MTLRenderPassDescriptor* passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; - passDescriptor.colorAttachments[0].texture = currentTexture; - passDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; - passDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; - passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); - - - id commandBuffer = [commandQueue commandBuffer]; - id commandEncoder = [commandBuffer - renderCommandEncoderWithDescriptor:passDescriptor]; - [commandEncoder endEncoding]; - [commandBuffer commit]; - } - - void Device::Present() { - id commandBuffer = [commandQueue commandBuffer]; - [commandBuffer presentDrawable: currentDrawable]; - [commandBuffer commit]; - } - id Device::GetMTLDevice() { return mtlDevice; } - id Device::GetCurrentTexture() { - return currentTexture; - } - id Device::GetPendingCommandBuffer() { if (pendingCommands == nil) { pendingCommands = [commandQueue commandBuffer]; diff --git a/src/backend/metal/SwapChainMTL.mm b/src/backend/metal/SwapChainMTL.mm index b762ddf508..91d1d9256e 100644 --- a/src/backend/metal/SwapChainMTL.mm +++ b/src/backend/metal/SwapChainMTL.mm @@ -14,6 +14,7 @@ #include "backend/metal/SwapChainMTL.h" +#include "backend/metal/MetalBackend.h" #include "backend/metal/TextureMTL.h" #include @@ -25,19 +26,23 @@ namespace metal { : SwapChainBase(builder) { const auto& im = GetImplementation(); nxtWSIContextMetal wsiContext = {}; - // TODO(kainino@chromium.org): set up wsiContext + wsiContext.device = ToBackend(GetDevice())->GetMTLDevice(); im.Init(im.userData, &wsiContext); - - // TODO(kainino@chromium.org): set up Metal swapchain } SwapChain::~SwapChain() { - // TODO(kainino@chromium.org): clean up Metal swapchain } TextureBase* SwapChain::GetNextTextureImpl(TextureBuilder* builder) { - id nativeTexture = nil; - // TODO(kainino@chromium.org): obtain MTLTexture from Metal swapchain + const auto& im = GetImplementation(); + nxtSwapChainNextTexture next = {}; + nxtSwapChainError error = im.GetNextTexture(im.userData, &next); + if (error) { + GetDevice()->HandleError(error); + return nullptr; + } + + id nativeTexture = reinterpret_cast>(next.texture); return new Texture(builder, nativeTexture); } diff --git a/src/backend/opengl/CommandBufferGL.cpp b/src/backend/opengl/CommandBufferGL.cpp index 9241b733f3..f977ff16aa 100644 --- a/src/backend/opengl/CommandBufferGL.cpp +++ b/src/backend/opengl/CommandBufferGL.cpp @@ -104,21 +104,13 @@ namespace opengl { glGenFramebuffers(1, ¤tFBO); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO); - auto* device = ToBackend(GetDevice()); const auto& info = currentRenderPass->GetSubpassInfo(currentSubpass); for (uint32_t index = 0; index < info.colorAttachments.size(); ++index) { uint32_t attachment = info.colorAttachments[index]; - // TODO(kainino@chromium.org): currently a 'null' texture view - // falls back to the 'back buffer' but this should go away - // when we have WSI. - GLuint texture = 0; - if (auto textureView = currentFramebuffer->GetTextureView(attachment)) { - texture = ToBackend(textureView->GetTexture())->GetHandle(); - } else { - texture = device->GetCurrentTexture(); - } + auto textureView = currentFramebuffer->GetTextureView(attachment); + GLuint texture = ToBackend(textureView->GetTexture())->GetHandle(); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, GL_TEXTURE_2D, texture, 0); @@ -131,6 +123,7 @@ namespace opengl { nxt::TextureFormat format = textureView->GetTexture()->GetFormat(); GLenum glAttachment = 0; + // TODO(kainino@chromium.org): it may be valid to just always use GL_DEPTH_STENCIL_ATTACHMENT here. if (TextureFormatHasDepth(format)) { if (TextureFormatHasStencil(format)) { glAttachment = GL_DEPTH_STENCIL_ATTACHMENT; diff --git a/src/backend/opengl/OpenGLBackend.cpp b/src/backend/opengl/OpenGLBackend.cpp index 831c721472..5136d7cc9f 100644 --- a/src/backend/opengl/OpenGLBackend.cpp +++ b/src/backend/opengl/OpenGLBackend.cpp @@ -30,11 +30,6 @@ namespace opengl { nxtProcTable GetNonValidatingProcs(); nxtProcTable GetValidatingProcs(); - void HACKCLEAR(nxtDevice device) { - Device* backendDevice = reinterpret_cast(device); - backendDevice->HACKCLEAR(); - } - void Init(void* (*getProc)(const char*), nxtProcTable* procs, nxtDevice* device) { *device = nullptr; @@ -44,17 +39,6 @@ namespace opengl { *device = reinterpret_cast(new Device); glEnable(GL_DEPTH_TEST); - HACKCLEAR(*device); - } - - void InitBackbuffer(nxtDevice device) { - Device* backendDevice = reinterpret_cast(device); - backendDevice->InitBackbuffer(); - } - - void CommitBackbuffer(nxtDevice device) { - Device* backendDevice = reinterpret_cast(device); - backendDevice->CommitBackbuffer(); } // Device @@ -117,36 +101,6 @@ namespace opengl { void Device::TickImpl() { } - void Device::HACKCLEAR() { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backFBO); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - } - - void Device::InitBackbuffer() { - glGenTextures(1, &backTexture); - glBindTexture(GL_TEXTURE_2D, backTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 640, 480, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - - glGenFramebuffers(1, &backFBO); - glBindFramebuffer(GL_READ_FRAMEBUFFER, backFBO); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, backTexture, 0); - - HACKCLEAR(); - } - - void Device::CommitBackbuffer() { - glBindFramebuffer(GL_READ_FRAMEBUFFER, backFBO); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBlitFramebuffer(0, 0, 640, 480, 0, 0, 640, 480, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - - GLuint Device::GetCurrentTexture() { - return backTexture; - } - // Bind Group BindGroup::BindGroup(BindGroupBuilder* builder) diff --git a/src/backend/opengl/OpenGLBackend.h b/src/backend/opengl/OpenGLBackend.h index a37637971b..25771a585c 100644 --- a/src/backend/opengl/OpenGLBackend.h +++ b/src/backend/opengl/OpenGLBackend.h @@ -104,15 +104,6 @@ namespace opengl { TextureViewBase* CreateTextureView(TextureViewBuilder* builder) override; void TickImpl() override; - - void HACKCLEAR(); - void InitBackbuffer(); - void CommitBackbuffer(); - GLuint GetCurrentTexture(); - - private: - GLuint backFBO = 0; - GLuint backTexture = 0; }; class BindGroup : public BindGroupBase { diff --git a/src/backend/opengl/SwapChainGL.cpp b/src/backend/opengl/SwapChainGL.cpp index d3e9bda2fc..e37234bad6 100644 --- a/src/backend/opengl/SwapChainGL.cpp +++ b/src/backend/opengl/SwapChainGL.cpp @@ -14,6 +14,7 @@ #include "backend/opengl/SwapChainGL.h" +#include "backend/Device.h" #include "backend/opengl/TextureGL.h" #include @@ -24,18 +25,21 @@ namespace opengl { SwapChain::SwapChain(SwapChainBuilder* builder) : SwapChainBase(builder) { const auto& im = GetImplementation(); - nxtWSIContextGL wsiContext = {}; - // TODO(kainino@chromium.org): set up wsiContext - im.Init(im.userData, &wsiContext); - - // TODO(kainino@chromium.org): set up FBO + im.Init(im.userData, nullptr); } SwapChain::~SwapChain() { - // TODO(kainino@chromium.org): clean up FBO } TextureBase* SwapChain::GetNextTextureImpl(TextureBuilder* builder) { + const auto& im = GetImplementation(); + nxtSwapChainNextTexture next = {}; + nxtSwapChainError error = im.GetNextTexture(im.userData, &next); + if (error) { + GetDevice()->HandleError(error); + return nullptr; + } + GLuint nativeTexture = static_cast(reinterpret_cast(next.texture)); return new Texture(builder, nativeTexture); } diff --git a/src/backend/opengl/SwapChainGL.h b/src/backend/opengl/SwapChainGL.h index 1990daf80a..24741ff04d 100644 --- a/src/backend/opengl/SwapChainGL.h +++ b/src/backend/opengl/SwapChainGL.h @@ -31,9 +31,6 @@ namespace opengl { protected: TextureBase* GetNextTextureImpl(TextureBuilder* builder) override; - - private: - GLuint nativeTexture = 0; }; } diff --git a/src/include/nxt/nxt_wsi.h b/src/include/nxt/nxt_wsi.h index f3d171e9c1..dfa1aa486d 100644 --- a/src/include/nxt/nxt_wsi.h +++ b/src/include/nxt/nxt_wsi.h @@ -19,6 +19,7 @@ // Error message (or nullptr if there was no error) typedef const char* nxtSwapChainError; +constexpr nxtSwapChainError NXT_SWAP_CHAIN_NO_ERROR = nullptr; typedef struct { /// Backend-specific texture id/name/pointer @@ -46,13 +47,17 @@ typedef struct { void* userData = nullptr; } nxtSwapChainImplementation; -#ifdef NXT_ENABLE_BACKEND_D3D12 +#if defined(NXT_ENABLE_BACKEND_D3D12) && defined(__cplusplus) typedef struct { + nxtDevice device = nullptr; } nxtWSIContextD3D12; #endif -#ifdef NXT_ENABLE_BACKEND_METAL +#if defined(NXT_ENABLE_BACKEND_METAL) && defined(__OBJC__) +#import + typedef struct { + id device = nil; } nxtWSIContextMetal; #endif diff --git a/src/tests/NXTTest.cpp b/src/tests/NXTTest.cpp index ee63d82159..00391d482d 100644 --- a/src/tests/NXTTest.cpp +++ b/src/tests/NXTTest.cpp @@ -137,6 +137,10 @@ void NXTTest::SetUp() { device = nxt::Device::Acquire(backendDevice); queue = device.CreateQueueBuilder().GetResult(); + swapchain = device.CreateSwapChainBuilder() + .SetImplementation(binding->GetSwapChainImplementation()) + .GetResult(); + device.SetErrorCallback(DeviceErrorCauseTestFailure, 0); } @@ -217,8 +221,11 @@ void NXTTest::WaitABit() { utils::USleep(100); } -void NXTTest::SwapBuffers() { - binding->SwapBuffers(); +void NXTTest::SwapBuffersForCapture() { + // Insert a frame boundary for API capture tools. + nxt::Texture backBuffer = swapchain.GetNextTexture(); + backBuffer.TransitionUsage(nxt::TextureUsageBit::Present); + swapchain.Present(backBuffer); } NXTTest::ReadbackReservation NXTTest::ReserveReadback(uint32_t readbackSize) { diff --git a/src/tests/NXTTest.h b/src/tests/NXTTest.h index f6a975c29b..8e12068299 100644 --- a/src/tests/NXTTest.h +++ b/src/tests/NXTTest.h @@ -77,13 +77,15 @@ class NXTTest : public ::testing::TestWithParam { protected: nxt::Device device; nxt::Queue queue; + nxt::SwapChain swapchain; // Helper methods to implement the EXPECT_ macros std::ostringstream& AddBufferExpectation(const char* file, int line, const nxt::Buffer& buffer, uint32_t offset, uint32_t size, detail::Expectation* expectation); std::ostringstream& AddTextureExpectation(const char* file, int line, const nxt::Texture& texture, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t level, uint32_t pixelSize, detail::Expectation* expectation); void WaitABit(); - void SwapBuffers(); + + void SwapBuffersForCapture(); private: // MapRead buffers used to get data for the expectations diff --git a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp index d1149d29fd..994215325e 100644 --- a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp +++ b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp @@ -21,7 +21,7 @@ class RenderPipelineValidationTest : public ValidationTest { void SetUp() override { ValidationTest::SetUp(); - utils::CreateDefaultRenderPass(device, &renderpass, &framebuffer); + CreateSimpleRenderPassAndFramebuffer(device, &renderpass, &framebuffer); pipelineLayout = device.CreatePipelineLayoutBuilder().GetResult(); diff --git a/src/tests/unittests/validation/ValidationTest.cpp b/src/tests/unittests/validation/ValidationTest.cpp index 7db14e05cb..1aba3a8b19 100644 --- a/src/tests/unittests/validation/ValidationTest.cpp +++ b/src/tests/unittests/validation/ValidationTest.cpp @@ -69,6 +69,32 @@ bool ValidationTest::EndExpectDeviceError() { return error; } +void ValidationTest::CreateSimpleRenderPassAndFramebuffer(const nxt::Device& device, nxt::RenderPass* renderpass, nxt::Framebuffer* framebuffer) { + auto colorBuffer = device.CreateTextureBuilder() + .SetDimension(nxt::TextureDimension::e2D) + .SetExtent(640, 480, 1) + .SetFormat(nxt::TextureFormat::R8G8B8A8Unorm) + .SetMipLevels(1) + .SetAllowedUsage(nxt::TextureUsageBit::OutputAttachment) + .GetResult(); + colorBuffer.FreezeUsage(nxt::TextureUsageBit::OutputAttachment); + auto colorView = colorBuffer.CreateTextureViewBuilder() + .GetResult(); + + *renderpass = device.CreateRenderPassBuilder() + .SetAttachmentCount(1) + .AttachmentSetFormat(0, nxt::TextureFormat::R8G8B8A8Unorm) + .SetSubpassCount(1) + .SubpassSetColorAttachment(0, 0, 0) + .GetResult(); + + *framebuffer = device.CreateFramebufferBuilder() + .SetRenderPass(*renderpass) + .SetDimensions(640, 480) + .SetAttachment(0, colorView) + .GetResult(); +} + void ValidationTest::OnDeviceError(const char* message, nxtCallbackUserdata userdata) { // Skip this one specific error that is raised when a builder is used after it got an error // this is important because we don't want to wrap all creation tests in ASSERT_DEVICE_ERROR. diff --git a/src/tests/unittests/validation/ValidationTest.h b/src/tests/unittests/validation/ValidationTest.h index 3a74791b13..2213d260b1 100644 --- a/src/tests/unittests/validation/ValidationTest.h +++ b/src/tests/unittests/validation/ValidationTest.h @@ -48,6 +48,8 @@ class ValidationTest : public testing::Test { void StartExpectDeviceError(); bool EndExpectDeviceError(); + void CreateSimpleRenderPassAndFramebuffer(const nxt::Device& device, nxt::RenderPass* renderpass, nxt::Framebuffer* framebuffer); + // Helper functions to create objects to test validation. struct DummyRenderPass { diff --git a/src/utils/BackendBinding.h b/src/utils/BackendBinding.h index ecbb427757..b40b21793e 100644 --- a/src/utils/BackendBinding.h +++ b/src/utils/BackendBinding.h @@ -15,6 +15,8 @@ #ifndef UTILS_BACKENDBINDING_H_ #define UTILS_BACKENDBINDING_H_ +#include + struct GLFWwindow; typedef struct nxtProcTable_s nxtProcTable; typedef struct nxtDeviceImpl* nxtDevice; @@ -35,7 +37,7 @@ namespace utils { virtual void SetupGLFWWindowHints() = 0; virtual void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) = 0; - virtual void SwapBuffers() = 0; + virtual uint64_t GetSwapChainImplementation() = 0; void SetWindow(GLFWwindow* window); diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 2dc1b66d9d..1d273ff400 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -19,6 +19,7 @@ list(APPEND UTILS_SOURCES ${UTILS_DIR}/BackendBinding.h ${UTILS_DIR}/NXTHelpers.cpp ${UTILS_DIR}/NXTHelpers.h + ${UTILS_DIR}/SwapChainImpl.h ${UTILS_DIR}/SystemUtils.cpp ${UTILS_DIR}/SystemUtils.h ) diff --git a/src/utils/D3D12Binding.cpp b/src/utils/D3D12Binding.cpp index 7600de3add..a2602950e2 100644 --- a/src/utils/D3D12Binding.cpp +++ b/src/utils/D3D12Binding.cpp @@ -15,6 +15,8 @@ #include "utils/BackendBinding.h" #include "common/Assert.h" +#include "nxt/nxt_wsi.h" +#include "utils/SwapChainImpl.h" #define GLFW_EXPOSE_NATIVE_WIN32 #include "GLFW/glfw3.h" @@ -34,7 +36,6 @@ namespace backend { namespace d3d12 { void Init(ComPtr d3d12Device, nxtProcTable* procs, nxtDevice* device); ComPtr GetCommandQueue(nxtDevice device); - void SetNextTexture(nxtDevice device, ComPtr resource); uint64_t GetSerial(const nxtDevice device); void NextSerial(nxtDevice device); void ExecuteCommandLists(nxtDevice device, std::initializer_list commandLists); @@ -44,48 +45,88 @@ namespace d3d12 { } namespace utils { + namespace { + void ASSERT_SUCCESS(HRESULT hr) { + ASSERT(SUCCEEDED(hr)); + } - class D3D12Binding : public BackendBinding { - public: - void SetupGLFWWindowHints() override { - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - } + ComPtr CreateFactory() { + ComPtr factory; - void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override { - uint32_t dxgiFactoryFlags = 0; + uint32_t dxgiFactoryFlags = 0; #ifdef _DEBUG - // Enable the debug layer (requires the Graphics Tools "optional feature"). - // NOTE: Enabling the debug layer after device creation will invalidate the active device. - { - ComPtr debugController; - if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) { - debugController->EnableDebugLayer(); + // Enable the debug layer (requires the Graphics Tools "optional feature"). + // NOTE: Enabling the debug layer after device creation will invalidate the active device. + { + ComPtr debugController; + if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)))) { + debugController->EnableDebugLayer(); - // Enable additional debug layers. - dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; - } - - ComPtr dxgiDebug; - if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug)))) { - dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_ALL)); - } + // Enable additional debug layers. + dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; } + + ComPtr dxgiDebug; + if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug)))) { + dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_ALL)); + } + } #endif - ASSERT_SUCCESS(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory))); - ASSERT(GetHardwareAdapter(factory.Get(), &hardwareAdapter)); - ASSERT_SUCCESS(D3D12CreateDevice( - hardwareAdapter.Get(), - D3D_FEATURE_LEVEL_11_0, - IID_PPV_ARGS(&d3d12Device) - )); + ASSERT_SUCCESS(CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory))); - backend::d3d12::Init(d3d12Device, procs, device); - backendDevice = *device; + return factory; + } + } + + class SwapChainImplD3D12 : SwapChainImpl { + public: + static nxtSwapChainImplementation Create(HWND window, const nxtProcTable& procs) { + auto impl = GenerateSwapChainImplementation(); + impl.userData = new SwapChainImplD3D12(window, procs); + return impl; + } + + private: + nxtDevice backendDevice = nullptr; + nxtProcTable procs = {}; + + static constexpr unsigned int kFrameCount = 2; + + HWND window = 0; + ComPtr factory = {}; + ComPtr commandQueue = {}; + ComPtr swapChain = {}; + ComPtr renderTargetResources[kFrameCount] = {}; + + // Frame synchronization. Updated every frame + uint32_t renderTargetIndex = 0; + uint32_t previousRenderTargetIndex = 0; + uint64_t lastSerialRenderTargetWasUsed[kFrameCount] = {}; + + SwapChainImplD3D12(HWND window, nxtProcTable procs) + : window(window), procs(procs), factory(CreateFactory()) { + } + + ~SwapChainImplD3D12() { + } + + // For GenerateSwapChainImplementation + friend class SwapChainImpl; + + void Init(nxtWSIContextD3D12* ctx) { + backendDevice = ctx->device; commandQueue = backend::d3d12::GetCommandQueue(backendDevice); + } + + nxtSwapChainError Configure(nxtTextureFormat format, + uint32_t width, uint32_t height) { + if (format != NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) { + return "unsupported format"; + } + ASSERT(width > 0); + ASSERT(height > 0); - int width, height; - glfwGetWindowSize(window, &width, &height); DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width = width; swapChainDesc.Height = height; @@ -96,11 +137,10 @@ namespace utils { swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; - HWND win32Window = glfwGetWin32Window(window); ComPtr swapChain1; ASSERT_SUCCESS(factory->CreateSwapChainForHwnd( commandQueue.Get(), - win32Window, + window, &swapChainDesc, nullptr, nullptr, @@ -122,48 +162,26 @@ namespace utils { lastSerialRenderTargetWasUsed[n] = initialSerial; } - // Transition the first frame to be a render target - { - backend::d3d12::OpenCommandList(backendDevice, &commandList); - - D3D12_RESOURCE_BARRIER resourceBarrier; - resourceBarrier.Transition.pResource = renderTargetResources[renderTargetIndex].Get(); - resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; - resourceBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; - resourceBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - resourceBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - commandList->ResourceBarrier(1, &resourceBarrier); - ASSERT_SUCCESS(commandList->Close()); - backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() }); - - backend::d3d12::NextSerial(backendDevice); - } - - backend::d3d12::SetNextTexture(backendDevice, renderTargetResources[renderTargetIndex]); + return NXT_SWAP_CHAIN_NO_ERROR; } - void SwapBuffers() override { - // Transition current frame's render target for presenting - { - backend::d3d12::OpenCommandList(backendDevice, &commandList); - D3D12_RESOURCE_BARRIER resourceBarrier; - resourceBarrier.Transition.pResource = renderTargetResources[renderTargetIndex].Get(); - resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; - resourceBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; - resourceBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - resourceBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - commandList->ResourceBarrier(1, &resourceBarrier); - ASSERT_SUCCESS(commandList->Close()); - backend::d3d12::ExecuteCommandLists(backendDevice, { commandList.Get() }); - } + nxtSwapChainError GetNextTexture(nxtSwapChainNextTexture* nextTexture) { + nextTexture->texture = renderTargetResources[renderTargetIndex].Get(); + return NXT_SWAP_CHAIN_NO_ERROR; + } + + nxtSwapChainError Present() { + // Current frame already transitioned to Present by the application, but + // we need to flush the D3D12 backend's pending transitions. + procs.deviceTick(backendDevice); ASSERT_SUCCESS(swapChain->Present(1, 0)); // Transition last frame's render target back to being a render target { + ComPtr commandList = {}; backend::d3d12::OpenCommandList(backendDevice, &commandList); + D3D12_RESOURCE_BARRIER resourceBarrier; resourceBarrier.Transition.pResource = renderTargetResources[previousRenderTargetIndex].Get(); resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; @@ -188,32 +206,47 @@ namespace utils { lastSerialRenderTargetWasUsed[renderTargetIndex] = backend::d3d12::GetSerial(backendDevice); - // Tell the backend to render to the current render target - backend::d3d12::SetNextTexture(backendDevice, renderTargetResources[renderTargetIndex]); + return NXT_SWAP_CHAIN_NO_ERROR; + } + }; + + class D3D12Binding : public BackendBinding { + public: + void SetupGLFWWindowHints() override { + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + } + + void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override { + factory = CreateFactory(); + ASSERT(GetHardwareAdapter(factory.Get(), &hardwareAdapter)); + ASSERT_SUCCESS(D3D12CreateDevice( + hardwareAdapter.Get(), + D3D_FEATURE_LEVEL_11_0, + IID_PPV_ARGS(&d3d12Device) + )); + + backend::d3d12::Init(d3d12Device, procs, device); + backendDevice = *device; + procTable = *procs; + } + + uint64_t GetSwapChainImplementation() override { + if (swapchainImpl.userData == nullptr) { + HWND win32Window = glfwGetWin32Window(window); + swapchainImpl = SwapChainImplD3D12::Create(win32Window, procTable); + } + return reinterpret_cast(&swapchainImpl); } private: nxtDevice backendDevice = nullptr; - - static constexpr unsigned int kFrameCount = 2; + nxtSwapChainImplementation swapchainImpl = {}; + nxtProcTable procTable = {}; // Initialization ComPtr factory; ComPtr hardwareAdapter; ComPtr d3d12Device; - ComPtr commandQueue; - ComPtr swapChain; - ComPtr renderTargetResources[kFrameCount]; - - // Frame synchronization. Updated every frame - uint32_t renderTargetIndex; - uint32_t previousRenderTargetIndex; - uint64_t lastSerialRenderTargetWasUsed[kFrameCount]; - ComPtr commandList; - - static void ASSERT_SUCCESS(HRESULT hr) { - ASSERT(SUCCEEDED(hr)); - } static bool GetHardwareAdapter(IDXGIFactory4* factory, IDXGIAdapter1** hardwareAdapter) { *hardwareAdapter = nullptr; diff --git a/src/utils/MetalBinding.mm b/src/utils/MetalBinding.mm index 74271fffcd..283188e6ca 100644 --- a/src/utils/MetalBinding.mm +++ b/src/utils/MetalBinding.mm @@ -14,6 +14,10 @@ #include "utils/BackendBinding.h" +#include "common/Assert.h" +#include "nxt/nxt_wsi.h" +#include "utils/SwapChainImpl.h" + #define GLFW_EXPOSE_NATIVE_COCOA #include "GLFW/glfw3.h" #include "GLFW/glfw3native.h" @@ -30,6 +34,103 @@ namespace metal { } namespace utils { + class SwapChainImplMTL : SwapChainImpl { + public: + static nxtSwapChainImplementation Create(id nswindow) { + auto impl = GenerateSwapChainImplementation(); + impl.userData = new SwapChainImplMTL(nswindow); + return impl; + } + + private: + id nsWindow = nil; + id mtlDevice = nil; + id commandQueue = nil; + + CAMetalLayer* layer = nullptr; + id currentDrawable = nil; + id currentTexture = nil; + + SwapChainImplMTL(id nsWindow) + : nsWindow(nsWindow) { + } + + ~SwapChainImplMTL() { + [currentTexture release]; + [currentDrawable release]; + } + + // For GenerateSwapChainImplementation + friend class SwapChainImpl; + + void Init(nxtWSIContextMetal* ctx) { + mtlDevice = ctx->device; + commandQueue = [mtlDevice newCommandQueue]; + } + + nxtSwapChainError Configure(nxtTextureFormat format, + uint32_t width, uint32_t height) { + if (format != NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) { + return "unsupported format"; + } + ASSERT(width > 0); + ASSERT(height > 0); + + NSView* contentView = [nsWindow contentView]; + [contentView setWantsLayer: YES]; + + CGSize size = {}; + size.width = width; + size.height = height; + + layer = [CAMetalLayer layer]; + [layer setDevice: mtlDevice]; + [layer setPixelFormat: MTLPixelFormatBGRA8Unorm]; + [layer setFramebufferOnly: YES]; + [layer setDrawableSize: size]; + + [contentView setLayer: layer]; + + return NXT_SWAP_CHAIN_NO_ERROR; + } + + nxtSwapChainError GetNextTexture(nxtSwapChainNextTexture* nextTexture) { + [currentDrawable release]; + currentDrawable = [layer nextDrawable]; + [currentDrawable retain]; + + [currentTexture release]; + currentTexture = currentDrawable.texture; + [currentTexture retain]; + + // Clear initial contents of the texture + { + MTLRenderPassDescriptor* passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + passDescriptor.colorAttachments[0].texture = currentTexture; + passDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; + passDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; + passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); + + id commandBuffer = [commandQueue commandBuffer]; + id commandEncoder = [commandBuffer + renderCommandEncoderWithDescriptor:passDescriptor]; + [commandEncoder endEncoding]; + [commandBuffer commit]; + } + + nextTexture->texture = reinterpret_cast(currentTexture); + + return NXT_SWAP_CHAIN_NO_ERROR; + } + + nxtSwapChainError Present() { + id commandBuffer = [commandQueue commandBuffer]; + [commandBuffer presentDrawable: currentDrawable]; + [commandBuffer commit]; + + return NXT_SWAP_CHAIN_NO_ERROR; + } + }; class MetalBinding : public BackendBinding { public: @@ -39,40 +140,21 @@ namespace utils { void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override { metalDevice = MTLCreateSystemDefaultDevice(); - id nsWindow = glfwGetCocoaWindow(window); - NSView* contentView = [nsWindow contentView]; - [contentView setWantsLayer: YES]; - - layer = [CAMetalLayer layer]; - [layer setDevice: metalDevice]; - [layer setPixelFormat: MTLPixelFormatBGRA8Unorm]; - [layer setFramebufferOnly: YES]; - [layer setDrawableSize: [contentView bounds].size]; - - [contentView setLayer: layer]; - backend::metal::Init(metalDevice, procs, device); backendDevice = *device; - - backend::metal::SetNextDrawable(backendDevice, GetNextDrawable()); } - void SwapBuffers() override { - backend::metal::Present(backendDevice); - backend::metal::SetNextDrawable(backendDevice, GetNextDrawable()); + + uint64_t GetSwapChainImplementation() override { + if (swapchainImpl.userData == nullptr) { + swapchainImpl = SwapChainImplMTL::Create(glfwGetCocoaWindow(window)); + } + return reinterpret_cast(&swapchainImpl); } private: - id GetNextDrawable() { - lastDrawable = [layer nextDrawable]; - return lastDrawable; - } - id metalDevice = nil; - CAMetalLayer* layer = nullptr; - - id lastDrawable = nil; - nxtDevice backendDevice = nullptr; + nxtSwapChainImplementation swapchainImpl = {}; }; BackendBinding* CreateMetalBinding() { diff --git a/src/utils/NXTHelpers.cpp b/src/utils/NXTHelpers.cpp index 7d5c9f9a6f..2080cad73b 100644 --- a/src/utils/NXTHelpers.cpp +++ b/src/utils/NXTHelpers.cpp @@ -92,34 +92,6 @@ namespace utils { return builder.GetResult(); } - void CreateDefaultRenderPass(const nxt::Device& device, nxt::RenderPass* renderPass, nxt::Framebuffer* framebuffer) { - auto depthStencilTexture = device.CreateTextureBuilder() - .SetDimension(nxt::TextureDimension::e2D) - .SetExtent(640, 480, 1) - .SetFormat(nxt::TextureFormat::D32FloatS8Uint) - .SetMipLevels(1) - .SetAllowedUsage(nxt::TextureUsageBit::OutputAttachment) - .GetResult(); - depthStencilTexture.FreezeUsage(nxt::TextureUsageBit::OutputAttachment); - auto depthStencilView = depthStencilTexture.CreateTextureViewBuilder() - .GetResult(); - - *renderPass = device.CreateRenderPassBuilder() - .SetAttachmentCount(2) - .AttachmentSetFormat(0, nxt::TextureFormat::R8G8B8A8Unorm) - .AttachmentSetFormat(1, nxt::TextureFormat::D32FloatS8Uint) - .SetSubpassCount(1) - .SubpassSetColorAttachment(0, 0, 0) - .SubpassSetDepthStencilAttachment(0, 1) - .GetResult(); - *framebuffer = device.CreateFramebufferBuilder() - .SetRenderPass(*renderPass) - .SetDimensions(640, 480) - // Attachment 0 is implicit until we add WSI - .SetAttachment(1, depthStencilView) - .GetResult(); - } - nxt::Buffer CreateFrozenBufferFromData(const nxt::Device& device, const void* data, uint32_t size, nxt::BufferUsageBit usage) { nxt::Buffer buffer = device.CreateBufferBuilder() .SetAllowedUsage(nxt::BufferUsageBit::TransferDst | usage) diff --git a/src/utils/NXTHelpers.h b/src/utils/NXTHelpers.h index a39eaffe2e..3ca50c1cdd 100644 --- a/src/utils/NXTHelpers.h +++ b/src/utils/NXTHelpers.h @@ -18,7 +18,6 @@ namespace utils { void FillShaderModuleBuilder(const nxt::ShaderModuleBuilder& builder, nxt::ShaderStage stage, const char* source); nxt::ShaderModule CreateShaderModule(const nxt::Device& device, nxt::ShaderStage stage, const char* source); - void CreateDefaultRenderPass(const nxt::Device& device, nxt::RenderPass* renderPass, nxt::Framebuffer* framebuffer); nxt::Buffer CreateFrozenBufferFromData(const nxt::Device& device, const void* data, uint32_t size, nxt::BufferUsageBit usage); } diff --git a/src/utils/NullBinding.cpp b/src/utils/NullBinding.cpp index 93364340e7..74ae78a7a4 100644 --- a/src/utils/NullBinding.cpp +++ b/src/utils/NullBinding.cpp @@ -29,7 +29,8 @@ namespace utils { void GetProcAndDevice(nxtProcTable* procs, nxtDevice* device) override { backend::null::Init(procs, device); } - void SwapBuffers() override { + uint64_t GetSwapChainImplementation() override { + return 0; } }; diff --git a/src/utils/OpenGLBinding.cpp b/src/utils/OpenGLBinding.cpp index b7b53172ec..ec66131e27 100644 --- a/src/utils/OpenGLBinding.cpp +++ b/src/utils/OpenGLBinding.cpp @@ -14,20 +14,104 @@ #include "utils/BackendBinding.h" +#include "common/Assert.h" #include "common/Platform.h" +#include "nxt/nxt_wsi.h" +#include "utils/SwapChainImpl.h" +#include +#include "glad/glad.h" #include "GLFW/glfw3.h" namespace backend { namespace opengl { void Init(void* (*getProc)(const char*), nxtProcTable* procs, nxtDevice* device); - void HACKCLEAR(nxtDevice device); - void InitBackbuffer(nxtDevice device); - void CommitBackbuffer(nxtDevice device); } } namespace utils { + class SwapChainImplGL : SwapChainImpl { + public: + static nxtSwapChainImplementation Create(GLFWwindow* window) { + auto impl = GenerateSwapChainImplementation(); + impl.userData = new SwapChainImplGL(window); + return impl; + } + + private: + GLFWwindow* window = nullptr; + uint32_t cfgWidth = 0; + uint32_t cfgHeight = 0; + GLuint backFBO = 0; + GLuint backTexture = 0; + + SwapChainImplGL(GLFWwindow* window) + : window(window) { + } + + ~SwapChainImplGL() { + glDeleteTextures(1, &backTexture); + glDeleteFramebuffers(1, &backFBO); + } + + void HACKCLEAR() { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backFBO); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + } + + // For GenerateSwapChainImplementation + friend class SwapChainImpl; + + void Init(nxtWSIContextGL*) { + glGenTextures(1, &backTexture); + glBindTexture(GL_TEXTURE_2D, backTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 0, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + glGenFramebuffers(1, &backFBO); + glBindFramebuffer(GL_READ_FRAMEBUFFER, backFBO); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, backTexture, 0); + } + + nxtSwapChainError Configure(nxtTextureFormat format, + uint32_t width, uint32_t height) { + if (format != NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) { + return "unsupported format"; + } + ASSERT(width > 0); + ASSERT(height > 0); + cfgWidth = width; + cfgHeight = height; + + glBindTexture(GL_TEXTURE_2D, backTexture); + // Reallocate the texture + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + // Clear the newly (re-)allocated texture + HACKCLEAR(); + + return NXT_SWAP_CHAIN_NO_ERROR; + } + + nxtSwapChainError GetNextTexture(nxtSwapChainNextTexture* nextTexture) { + nextTexture->texture = reinterpret_cast(static_cast(backTexture)); + return NXT_SWAP_CHAIN_NO_ERROR; + } + + nxtSwapChainError Present() { + glBindFramebuffer(GL_READ_FRAMEBUFFER, backFBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, cfgWidth, cfgHeight, 0, 0, cfgWidth, cfgHeight, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + glfwSwapBuffers(window); + HACKCLEAR(); + + return NXT_SWAP_CHAIN_NO_ERROR; + } + }; + class OpenGLBinding : public BackendBinding { public: void SetupGLFWWindowHints() override { @@ -48,16 +132,18 @@ namespace utils { backend::opengl::Init(reinterpret_cast(glfwGetProcAddress), procs, device); backendDevice = *device; - backend::opengl::InitBackbuffer(backendDevice); } - void SwapBuffers() override { - backend::opengl::CommitBackbuffer(backendDevice); - glfwSwapBuffers(window); - backend::opengl::HACKCLEAR(backendDevice); + + uint64_t GetSwapChainImplementation() override { + if (swapchainImpl.userData == nullptr) { + swapchainImpl = SwapChainImplGL::Create(window); + } + return reinterpret_cast(&swapchainImpl); } private: nxtDevice backendDevice = nullptr; + nxtSwapChainImplementation swapchainImpl = {}; }; BackendBinding* CreateOpenGLBinding() { diff --git a/src/utils/SwapChainImpl.h b/src/utils/SwapChainImpl.h new file mode 100644 index 0000000000..e1d2a37cbb --- /dev/null +++ b/src/utils/SwapChainImpl.h @@ -0,0 +1,47 @@ +// Copyright 2017 The NXT Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_SWAPCHAINIMPL_H_ +#define UTILS_SWAPCHAINIMPL_H_ + +namespace utils { + class SwapChainImpl { + protected: + template + static nxtSwapChainImplementation GenerateSwapChainImplementation() { + nxtSwapChainImplementation impl = {}; + impl.Init = [](void* userData, void* wsiContext) { + auto* ctx = reinterpret_cast(wsiContext); + reinterpret_cast(userData)->Init(ctx); + }; + impl.Destroy = [](void* userData) { + delete reinterpret_cast(userData); + }; + impl.Configure = [](void* userData, nxtTextureFormat format, uint32_t width, uint32_t height) { + return reinterpret_cast(userData)->Configure( + format, width, height); + }; + impl.GetNextTexture = [](void* userData, nxtSwapChainNextTexture* nextTexture) { + return reinterpret_cast(userData)->GetNextTexture( + nextTexture); + }; + impl.Present = [](void* userData) { + return reinterpret_cast(userData)->Present(); + }; + return impl; + } + }; +} + +#endif // UTILS_SWAPCHAINIMPL_H_