diff --git a/src/backend/CMakeLists.txt b/src/backend/CMakeLists.txt index 6229f6b34c..515f955a47 100644 --- a/src/backend/CMakeLists.txt +++ b/src/backend/CMakeLists.txt @@ -293,6 +293,8 @@ if (NXT_ENABLE_VULKAN) ${VULKAN_DIR}/BufferUploader.h ${VULKAN_DIR}/BufferVk.cpp ${VULKAN_DIR}/BufferVk.h + ${VULKAN_DIR}/CommandBufferVk.cpp + ${VULKAN_DIR}/CommandBufferVk.h ${VULKAN_DIR}/FencedDeleter.cpp ${VULKAN_DIR}/FencedDeleter.h ${VULKAN_DIR}/MemoryAllocator.cpp diff --git a/src/backend/Commands.h b/src/backend/Commands.h index 18a7d6e2ea..8564d7f585 100644 --- a/src/backend/Commands.h +++ b/src/backend/Commands.h @@ -168,6 +168,7 @@ namespace backend { // This needs to be called before the CommandIterator is freed so that the Ref<> present in // the commands have a chance to run their destructor and remove internal references. + class CommandIterator; void FreeCommands(CommandIterator* commands); void SkipCommand(CommandIterator* commands, Command type); diff --git a/src/backend/d3d12/CommandBufferD3D12.h b/src/backend/d3d12/CommandBufferD3D12.h index 224b3bbeec..8af74d37fe 100644 --- a/src/backend/d3d12/CommandBufferD3D12.h +++ b/src/backend/d3d12/CommandBufferD3D12.h @@ -35,6 +35,7 @@ namespace backend { namespace d3d12 { Device* mDevice; CommandIterator mCommands; }; + }} // namespace backend::d3d12 #endif // BACKEND_D3D12_COMMANDBUFFERD3D12_H_ diff --git a/src/backend/vulkan/BufferVk.cpp b/src/backend/vulkan/BufferVk.cpp index a4e94de270..8eae6b3e27 100644 --- a/src/backend/vulkan/BufferVk.cpp +++ b/src/backend/vulkan/BufferVk.cpp @@ -102,6 +102,10 @@ namespace backend { namespace vulkan { CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_READ_STATUS_SUCCESS, data); } + VkBuffer Buffer::GetHandle() const { + return mHandle; + } + void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) { BufferUploader* uploader = ToBackend(GetDevice())->GetBufferUploader(); uploader->BufferSubData(mHandle, start * sizeof(uint32_t), count * sizeof(uint32_t), data); diff --git a/src/backend/vulkan/BufferVk.h b/src/backend/vulkan/BufferVk.h index 288a576784..a7a31cf86d 100644 --- a/src/backend/vulkan/BufferVk.h +++ b/src/backend/vulkan/BufferVk.h @@ -32,6 +32,8 @@ namespace backend { namespace vulkan { void OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data); + VkBuffer GetHandle() const; + private: void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override; void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override; diff --git a/src/backend/vulkan/CommandBufferVk.cpp b/src/backend/vulkan/CommandBufferVk.cpp new file mode 100644 index 0000000000..75e6f824ac --- /dev/null +++ b/src/backend/vulkan/CommandBufferVk.cpp @@ -0,0 +1,63 @@ +// 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. + +#include "backend/vulkan/CommandBufferVk.h" + +#include "backend/Commands.h" +#include "backend/vulkan/BufferVk.h" +#include "backend/vulkan/VulkanBackend.h" + +#include + +namespace backend { namespace vulkan { + + CommandBuffer::CommandBuffer(CommandBufferBuilder* builder) + : CommandBufferBase(builder), mCommands(builder->AcquireCommands()) { + } + + CommandBuffer::~CommandBuffer() { + FreeCommands(&mCommands); + } + + void CommandBuffer::RecordCommands(VkCommandBuffer commands) { + Device* device = ToBackend(GetDevice()); + + Command type; + while (mCommands.NextCommandId(&type)) { + switch (type) { + case Command::CopyBufferToBuffer: { + CopyBufferToBufferCmd* copy = mCommands.NextCommand(); + auto& src = copy->source; + auto& dst = copy->destination; + + VkBufferCopy region; + region.srcOffset = src.offset; + region.dstOffset = dst.offset; + region.size = copy->size; + + VkBuffer srcHandle = ToBackend(src.buffer)->GetHandle(); + VkBuffer dstHandle = ToBackend(dst.buffer)->GetHandle(); + device->fn.CmdCopyBuffer(commands, srcHandle, dstHandle, 1, ®ion); + } break; + + case Command::TransitionBufferUsage: { + SkipCommand(&mCommands, type); + } break; + + default: { UNREACHABLE(); } break; + } + } + } + +}} // namespace backend::vulkan diff --git a/src/backend/vulkan/CommandBufferVk.h b/src/backend/vulkan/CommandBufferVk.h new file mode 100644 index 0000000000..b5a387a7f1 --- /dev/null +++ b/src/backend/vulkan/CommandBufferVk.h @@ -0,0 +1,37 @@ +// 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 BACKEND_VULKAN_COMMANDBUFFERVK_H_ +#define BACKEND_VULKAN_COMMANDBUFFERVK_H_ + +#include "backend/CommandBuffer.h" + +#include "backend/vulkan/vulkan_platform.h" + +namespace backend { namespace vulkan { + + class CommandBuffer : public CommandBufferBase { + public: + CommandBuffer(CommandBufferBuilder* builder); + ~CommandBuffer(); + + void RecordCommands(VkCommandBuffer commands); + + private: + CommandIterator mCommands; + }; + +}} // namespace backend::vulkan + +#endif // BACKEND_VULKAN_COMMANDBUFFERVK_H_ diff --git a/src/backend/vulkan/GeneratedCodeIncludes.h b/src/backend/vulkan/GeneratedCodeIncludes.h index 2725c0adba..cc2dc4a082 100644 --- a/src/backend/vulkan/GeneratedCodeIncludes.h +++ b/src/backend/vulkan/GeneratedCodeIncludes.h @@ -13,4 +13,5 @@ // limitations under the License. #include "backend/vulkan/BufferVk.h" +#include "backend/vulkan/CommandBufferVk.h" #include "backend/vulkan/VulkanBackend.h" diff --git a/src/backend/vulkan/VulkanBackend.cpp b/src/backend/vulkan/VulkanBackend.cpp index ca54465ad5..fd3b722a1a 100644 --- a/src/backend/vulkan/VulkanBackend.cpp +++ b/src/backend/vulkan/VulkanBackend.cpp @@ -17,6 +17,7 @@ #include "backend/Commands.h" #include "backend/vulkan/BufferUploader.h" #include "backend/vulkan/BufferVk.h" +#include "backend/vulkan/CommandBufferVk.h" #include "backend/vulkan/FencedDeleter.h" #include "common/Platform.h" @@ -246,6 +247,11 @@ namespace backend { namespace vulkan { if (mPendingCommands.pool != VK_NULL_HANDLE) { SubmitPendingCommands(); + } else if (mCompletedSerial == mNextSerial - 1) { + // If there's no GPU work in flight we still need to artificially increment the serial + // so that CPU operations waiting on GPU completion can know they don't have to wait. + mCompletedSerial++; + mNextSerial++; } } @@ -584,7 +590,15 @@ namespace backend { namespace vulkan { Queue::~Queue() { } - void Queue::Submit(uint32_t, CommandBuffer* const*) { + void Queue::Submit(uint32_t numCommands, CommandBuffer* const* commands) { + Device* device = ToBackend(GetDevice()); + + VkCommandBuffer commandBuffer = device->GetPendingCommandBuffer(); + for (uint32_t i = 0; i < numCommands; ++i) { + commands[i]->RecordCommands(commandBuffer); + } + + device->SubmitPendingCommands(); } // Texture diff --git a/src/backend/vulkan/VulkanBackend.h b/src/backend/vulkan/VulkanBackend.h index dd49a78205..0af323476b 100644 --- a/src/backend/vulkan/VulkanBackend.h +++ b/src/backend/vulkan/VulkanBackend.h @@ -20,7 +20,6 @@ #include "backend/BindGroup.h" #include "backend/BindGroupLayout.h" #include "backend/BlendState.h" -#include "backend/CommandBuffer.h" #include "backend/ComputePipeline.h" #include "backend/DepthStencilState.h" #include "backend/Device.h" @@ -50,7 +49,7 @@ namespace backend { namespace vulkan { using BlendState = BlendStateBase; class Buffer; using BufferView = BufferViewBase; - using CommandBuffer = CommandBufferBase; + class CommandBuffer; using ComputePipeline = ComputePipelineBase; using DepthStencilState = DepthStencilStateBase; class Device; diff --git a/src/tests/end2end/BufferTests.cpp b/src/tests/end2end/BufferTests.cpp index b4626ad097..ea8cefd2be 100644 --- a/src/tests/end2end/BufferTests.cpp +++ b/src/tests/end2end/BufferTests.cpp @@ -136,10 +136,10 @@ TEST_P(BufferSetSubDataTests, SmallDataAtOffset) { // Stress test for many calls to SetSubData TEST_P(BufferSetSubDataTests, ManySetSubData) { - if (IsD3D12() || IsMetal()) { + if (IsD3D12() || IsMetal() || IsVulkan()) { // TODO(cwallez@chromium.org): Use ringbuffers for SetSubData on explicit APIs. // otherwise this creates too many resources and can take freeze the driver(?) - std::cout << "Test skipped on D3D12 and Metal" << std::endl; + std::cout << "Test skipped on D3D12, Metal and Vulkan" << std::endl; return; } @@ -180,4 +180,8 @@ TEST_P(BufferSetSubDataTests, LargeSetSubData) { EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); } -NXT_INSTANTIATE_TEST(BufferSetSubDataTests, D3D12Backend, MetalBackend, OpenGLBackend) +NXT_INSTANTIATE_TEST(BufferSetSubDataTests, + D3D12Backend, + MetalBackend, + OpenGLBackend, + VulkanBackend)