diff --git a/examples/Animometer.cpp b/examples/Animometer.cpp index d742ec147c..86802a08ed 100644 --- a/examples/Animometer.cpp +++ b/examples/Animometer.cpp @@ -49,7 +49,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); nxt::ShaderModule vsModule = utils::CreateShaderModule(device, nxt::ShaderStage::Vertex, R"( #version 450 diff --git a/examples/CHelloTriangle.cpp b/examples/CHelloTriangle.cpp index 637f5d0496..9b68052ce1 100644 --- a/examples/CHelloTriangle.cpp +++ b/examples/CHelloTriangle.cpp @@ -39,7 +39,7 @@ void init() { swapchain = nxtSwapChainBuilderGetResult(builder); nxtSwapChainBuilderRelease(builder); } - nxtSwapChainConfigure(swapchain, NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM, 640, 480); + nxtSwapChainConfigure(swapchain, NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM, NXT_TEXTURE_USAGE_BIT_OUTPUT_ATTACHMENT, NXT_TEXTURE_USAGE_BIT_OUTPUT_ATTACHMENT, 640, 480); const char* vs = "#version 450\n" diff --git a/examples/ComputeBoids.cpp b/examples/ComputeBoids.cpp index 1ad62e31d5..52ad72100f 100644 --- a/examples/ComputeBoids.cpp +++ b/examples/ComputeBoids.cpp @@ -291,7 +291,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); initBuffers(); initRender(); diff --git a/examples/HelloCompute.cpp b/examples/HelloCompute.cpp index 2fbf3993f4..5db366bc24 100644 --- a/examples/HelloCompute.cpp +++ b/examples/HelloCompute.cpp @@ -35,7 +35,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); struct {uint32_t a; float b;} s; memset(&s, 0, sizeof(s)); diff --git a/examples/HelloDepthStencil.cpp b/examples/HelloDepthStencil.cpp index b8e9f24926..97d8d69db3 100644 --- a/examples/HelloDepthStencil.cpp +++ b/examples/HelloDepthStencil.cpp @@ -116,7 +116,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); initBuffers(); diff --git a/examples/HelloIndices.cpp b/examples/HelloIndices.cpp index dcd75e1fa3..93092f7e32 100644 --- a/examples/HelloIndices.cpp +++ b/examples/HelloIndices.cpp @@ -49,7 +49,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); initBuffers(); diff --git a/examples/HelloInstancing.cpp b/examples/HelloInstancing.cpp index 19ae47d7cb..a5c11f88d9 100644 --- a/examples/HelloInstancing.cpp +++ b/examples/HelloInstancing.cpp @@ -52,7 +52,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); initBuffers(); diff --git a/examples/HelloTriangle.cpp b/examples/HelloTriangle.cpp index 8d491f7637..b9c9334738 100644 --- a/examples/HelloTriangle.cpp +++ b/examples/HelloTriangle.cpp @@ -83,7 +83,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); initBuffers(); initTextures(); diff --git a/examples/HelloUBO.cpp b/examples/HelloUBO.cpp index bfa4c54b62..a01b1306dc 100644 --- a/examples/HelloUBO.cpp +++ b/examples/HelloUBO.cpp @@ -33,7 +33,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); nxt::ShaderModule vsModule = utils::CreateShaderModule(device, nxt::ShaderStage::Vertex, R"( #version 450 diff --git a/examples/HelloVertices.cpp b/examples/HelloVertices.cpp index 129627b8c7..8b93742c5b 100644 --- a/examples/HelloVertices.cpp +++ b/examples/HelloVertices.cpp @@ -43,7 +43,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); initBuffers(); diff --git a/examples/RenderToTexture.cpp b/examples/RenderToTexture.cpp index 93e220070d..de9df40ea9 100644 --- a/examples/RenderToTexture.cpp +++ b/examples/RenderToTexture.cpp @@ -167,7 +167,7 @@ void init() { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); initBuffers(); initTextures(); diff --git a/examples/glTFViewer/glTFViewer.cpp b/examples/glTFViewer/glTFViewer.cpp index 567bdadb36..cbf343986b 100644 --- a/examples/glTFViewer/glTFViewer.cpp +++ b/examples/glTFViewer/glTFViewer.cpp @@ -464,7 +464,7 @@ namespace { queue = device.CreateQueueBuilder().GetResult(); swapchain = GetSwapChain(device); - swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, 640, 480); + swapchain.Configure(nxt::TextureFormat::R8G8B8A8Unorm, nxt::TextureUsageBit::OutputAttachment, nxt::TextureUsageBit::OutputAttachment, 640, 480); renderpass = CreateDefaultRenderPass(device); depthStencilView = CreateDefaultDepthStencilView(device); diff --git a/next.json b/next.json index 13eafe9834..002b2f1c5c 100644 --- a/next.json +++ b/next.json @@ -1050,6 +1050,8 @@ "name": "configure", "args": [ {"name": "format", "type": "texture format"}, + {"name": "allowedUsage", "type": "texture usage bit"}, + {"name": "initialUsage", "type": "texture usage bit"}, {"name": "width", "type": "uint32_t"}, {"name": "height", "type": "uint32_t"} ] diff --git a/src/backend/SwapChain.cpp b/src/backend/SwapChain.cpp index 3ded8af929..4224ffb13d 100644 --- a/src/backend/SwapChain.cpp +++ b/src/backend/SwapChain.cpp @@ -34,17 +34,24 @@ namespace backend { return device; } - void SwapChainBase::Configure(nxt::TextureFormat format, uint32_t width, uint32_t height) { + void SwapChainBase::Configure(nxt::TextureFormat format, nxt::TextureUsageBit allowedUsage, nxt::TextureUsageBit initialUsage, uint32_t width, uint32_t height) { if (width == 0 || height == 0) { device->HandleError("Swap chain cannot be configured to zero size"); return; } + allowedUsage |= nxt::TextureUsageBit::Present; + if (!(HasZeroOrOneBits(initialUsage) && (initialUsage & allowedUsage))) { + device->HandleError("Swap chain configured with invalid texture usage"); + return; + } this->format = format; + this->allowedUsage = allowedUsage; + this->initialUsage = initialUsage; this->width = width; this->height = height; implementation.Configure(implementation.userData, - static_cast(format), width, height); + static_cast(format), static_cast(allowedUsage), static_cast(initialUsage), width, height); } TextureBase* SwapChainBase::GetNextTexture() { @@ -59,8 +66,8 @@ namespace backend { builder->SetExtent(width, height, 1); builder->SetFormat(format); builder->SetMipLevels(1); - builder->SetAllowedUsage(nxt::TextureUsageBit::OutputAttachment | nxt::TextureUsageBit::Present); - builder->SetInitialUsage(nxt::TextureUsageBit::OutputAttachment); + builder->SetAllowedUsage(allowedUsage); + builder->SetInitialUsage(initialUsage); auto* texture = GetNextTextureImpl(builder); lastNextTexture = texture; diff --git a/src/backend/SwapChain.h b/src/backend/SwapChain.h index 2e178559b6..77006a6047 100644 --- a/src/backend/SwapChain.h +++ b/src/backend/SwapChain.h @@ -32,7 +32,7 @@ namespace backend { DeviceBase* GetDevice(); // NXT API - void Configure(nxt::TextureFormat format, uint32_t width, uint32_t height); + void Configure(nxt::TextureFormat format, nxt::TextureUsageBit allowedUsage, nxt::TextureUsageBit initialUsage, uint32_t width, uint32_t height); TextureBase* GetNextTexture(); void Present(TextureBase* texture); @@ -44,6 +44,8 @@ namespace backend { DeviceBase* device = nullptr; nxtSwapChainImplementation implementation = {}; nxt::TextureFormat format = {}; + nxt::TextureUsageBit allowedUsage; + nxt::TextureUsageBit initialUsage; uint32_t width = 0; uint32_t height = 0; TextureBase* lastNextTexture = nullptr; diff --git a/src/backend/Texture.cpp b/src/backend/Texture.cpp index fbae12d84a..ab7e81f34b 100644 --- a/src/backend/Texture.cpp +++ b/src/backend/Texture.cpp @@ -58,6 +58,7 @@ namespace backend { bool TextureFormatHasDepthOrStencil(nxt::TextureFormat format) { switch (format) { case nxt::TextureFormat::R8G8B8A8Unorm: + case nxt::TextureFormat::R8G8B8A8Uint: return false; case nxt::TextureFormat::D32FloatS8Uint: return true; diff --git a/src/backend/opengl/TextureGL.cpp b/src/backend/opengl/TextureGL.cpp index 409211c5b0..fe8326ac64 100644 --- a/src/backend/opengl/TextureGL.cpp +++ b/src/backend/opengl/TextureGL.cpp @@ -37,6 +37,8 @@ namespace opengl { switch (format) { case nxt::TextureFormat::R8G8B8A8Unorm: return {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}; + case nxt::TextureFormat::R8G8B8A8Uint: + return { GL_RGBA8UI, GL_RGBA, GL_UNSIGNED_INT }; case nxt::TextureFormat::D32FloatS8Uint: return {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV}; default: diff --git a/src/include/nxt/nxt_wsi.h b/src/include/nxt/nxt_wsi.h index dfa1aa486d..85902813f6 100644 --- a/src/include/nxt/nxt_wsi.h +++ b/src/include/nxt/nxt_wsi.h @@ -35,7 +35,7 @@ typedef struct { void (*Destroy)(void* userData); /// Configure/reconfigure the swap chain. - nxtSwapChainError (*Configure)(void* userData, nxtTextureFormat format, uint32_t width, uint32_t height); + nxtSwapChainError (*Configure)(void* userData, nxtTextureFormat format, nxtTextureUsageBit allowedUsage, nxtTextureUsageBit initialUsage, uint32_t width, uint32_t height); /// Acquire the next texture from the swap chain. nxtSwapChainError (*GetNextTexture)(void* userData, nxtSwapChainNextTexture* nextTexture); diff --git a/src/utils/D3D12Binding.cpp b/src/utils/D3D12Binding.cpp index a2602950e2..fe90d70312 100644 --- a/src/utils/D3D12Binding.cpp +++ b/src/utils/D3D12Binding.cpp @@ -77,6 +77,41 @@ namespace utils { return factory; } + + DXGI_USAGE D3D12SwapChainBufferUsage(nxtTextureUsageBit allowedUsages) { + DXGI_USAGE usage = DXGI_CPU_ACCESS_NONE; + if (allowedUsages & NXT_TEXTURE_USAGE_BIT_SAMPLED) { + usage |= DXGI_USAGE_SHADER_INPUT; + } + if (allowedUsages & NXT_TEXTURE_USAGE_BIT_STORAGE) { + usage |= DXGI_USAGE_UNORDERED_ACCESS; + } + if (allowedUsages & NXT_TEXTURE_USAGE_BIT_OUTPUT_ATTACHMENT) { + usage |= DXGI_USAGE_RENDER_TARGET_OUTPUT; + } + return usage; + } + + D3D12_RESOURCE_STATES D3D12ResourceState(nxtTextureUsageBit usage) { + D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON; + if (usage & NXT_TEXTURE_USAGE_BIT_TRANSFER_SRC) { + resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE; + } + if (usage & NXT_TEXTURE_USAGE_BIT_TRANSFER_DST) { + resourceState |= D3D12_RESOURCE_STATE_COPY_DEST; + } + if (usage & NXT_TEXTURE_USAGE_BIT_SAMPLED) { + resourceState |= (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + } + if (usage & NXT_TEXTURE_USAGE_BIT_STORAGE) { + resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + } + if (usage & NXT_TEXTURE_USAGE_BIT_OUTPUT_ATTACHMENT) { + resourceState |= D3D12_RESOURCE_STATE_RENDER_TARGET; + } + + return resourceState; + } } class SwapChainImplD3D12 : SwapChainImpl { @@ -104,6 +139,8 @@ namespace utils { uint32_t previousRenderTargetIndex = 0; uint64_t lastSerialRenderTargetWasUsed[kFrameCount] = {}; + D3D12_RESOURCE_STATES renderTargetResourceState; + SwapChainImplD3D12(HWND window, nxtProcTable procs) : window(window), procs(procs), factory(CreateFactory()) { } @@ -119,7 +156,7 @@ namespace utils { commandQueue = backend::d3d12::GetCommandQueue(backendDevice); } - nxtSwapChainError Configure(nxtTextureFormat format, + nxtSwapChainError Configure(nxtTextureFormat format, nxtTextureUsageBit allowedUsage, nxtTextureUsageBit initialUsage, uint32_t width, uint32_t height) { if (format != NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) { return "unsupported format"; @@ -131,7 +168,7 @@ namespace utils { swapChainDesc.Width = width; swapChainDesc.Height = height; swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferUsage = D3D12SwapChainBufferUsage(allowedUsage); swapChainDesc.BufferCount = kFrameCount; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.SampleDesc.Count = 1; @@ -162,6 +199,25 @@ namespace utils { lastSerialRenderTargetWasUsed[n] = initialSerial; } + renderTargetResourceState = D3D12ResourceState(initialUsage); + + // Transition the first frame. Resources are initially created in PRESENT state + if (renderTargetResourceState != D3D12_RESOURCE_STATE_PRESENT) { + ComPtr commandList = {}; + 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 = renderTargetResourceState; + 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() }); + } + return NXT_SWAP_CHAIN_NO_ERROR; } @@ -178,14 +234,14 @@ namespace utils { ASSERT_SUCCESS(swapChain->Present(1, 0)); // Transition last frame's render target back to being a render target - { + if (renderTargetResourceState != D3D12_RESOURCE_STATE_PRESENT) { ComPtr commandList = {}; backend::d3d12::OpenCommandList(backendDevice, &commandList); D3D12_RESOURCE_BARRIER resourceBarrier; resourceBarrier.Transition.pResource = renderTargetResources[previousRenderTargetIndex].Get(); resourceBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; - resourceBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + resourceBarrier.Transition.StateAfter = renderTargetResourceState; resourceBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; resourceBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resourceBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; diff --git a/src/utils/MetalBinding.mm b/src/utils/MetalBinding.mm index 027b2c004b..89e0bce47c 100644 --- a/src/utils/MetalBinding.mm +++ b/src/utils/MetalBinding.mm @@ -68,7 +68,7 @@ namespace utils { commandQueue = [mtlDevice newCommandQueue]; } - nxtSwapChainError Configure(nxtTextureFormat format, + nxtSwapChainError Configure(nxtTextureFormat format, nxtTextureUsageBit, nxtTextureUsageBit, uint32_t width, uint32_t height) { if (format != NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) { return "unsupported format"; diff --git a/src/utils/OpenGLBinding.cpp b/src/utils/OpenGLBinding.cpp index 1b23f3f0e1..4aabe4db2f 100644 --- a/src/utils/OpenGLBinding.cpp +++ b/src/utils/OpenGLBinding.cpp @@ -69,7 +69,7 @@ namespace utils { GL_TEXTURE_2D, backTexture, 0); } - nxtSwapChainError Configure(nxtTextureFormat format, + nxtSwapChainError Configure(nxtTextureFormat format, nxtTextureUsageBit, nxtTextureUsageBit, uint32_t width, uint32_t height) { if (format != NXT_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) { return "unsupported format"; diff --git a/src/utils/SwapChainImpl.h b/src/utils/SwapChainImpl.h index e1d2a37cbb..057044189a 100644 --- a/src/utils/SwapChainImpl.h +++ b/src/utils/SwapChainImpl.h @@ -28,9 +28,9 @@ namespace utils { impl.Destroy = [](void* userData) { delete reinterpret_cast(userData); }; - impl.Configure = [](void* userData, nxtTextureFormat format, uint32_t width, uint32_t height) { + impl.Configure = [](void* userData, nxtTextureFormat format, nxtTextureUsageBit allowedUsage, nxtTextureUsageBit initialUsage, uint32_t width, uint32_t height) { return reinterpret_cast(userData)->Configure( - format, width, height); + format, allowedUsage, initialUsage, width, height); }; impl.GetNextTexture = [](void* userData, nxtSwapChainNextTexture* nextTexture) { return reinterpret_cast(userData)->GetNextTexture(