2025-01-29 07:38:52 -08:00
|
|
|
/* Copyright (C) 2025 Wildfire Games.
|
2023-12-02 16:30:12 -08:00
|
|
|
* This file is part of 0 A.D.
|
2023-01-10 12:22:20 -08:00
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
2023-01-10 12:22:20 -08:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
2023-01-10 12:22:20 -08:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2023-12-02 16:30:12 -08:00
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
2023-01-10 12:22:20 -08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
|
|
#include "DeviceCommandContext.h"
|
|
|
|
|
|
2023-02-01 14:09:25 -08:00
|
|
|
#include "lib/bits.h"
|
2023-01-10 12:22:20 -08:00
|
|
|
#include "maths/MathUtil.h"
|
|
|
|
|
#include "ps/CLogger.h"
|
2023-06-05 09:32:18 -07:00
|
|
|
#include "ps/ConfigDB.h"
|
2023-01-10 12:22:20 -08:00
|
|
|
#include "ps/containers/Span.h"
|
|
|
|
|
#include "ps/containers/StaticVector.h"
|
|
|
|
|
#include "renderer/backend/vulkan/Buffer.h"
|
|
|
|
|
#include "renderer/backend/vulkan/DescriptorManager.h"
|
|
|
|
|
#include "renderer/backend/vulkan/Device.h"
|
|
|
|
|
#include "renderer/backend/vulkan/Framebuffer.h"
|
2024-12-09 13:47:15 -08:00
|
|
|
#include "renderer/backend/vulkan/Mapping.h"
|
2023-01-10 12:22:20 -08:00
|
|
|
#include "renderer/backend/vulkan/PipelineState.h"
|
|
|
|
|
#include "renderer/backend/vulkan/RingCommandContext.h"
|
|
|
|
|
#include "renderer/backend/vulkan/ShaderProgram.h"
|
|
|
|
|
#include "renderer/backend/vulkan/Texture.h"
|
|
|
|
|
#include "renderer/backend/vulkan/Utilities.h"
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
2023-02-01 14:09:25 -08:00
|
|
|
#include <cstddef>
|
2023-02-17 09:36:10 -08:00
|
|
|
#include <tuple>
|
2023-01-10 12:22:20 -08:00
|
|
|
|
|
|
|
|
namespace Renderer
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
namespace Backend
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
namespace Vulkan
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
|
2024-12-09 13:47:15 -08:00
|
|
|
constexpr uint32_t UNIFORM_BUFFER_INITIAL_SIZE = 1024 * 1024 * 32;
|
2023-02-01 14:09:25 -08:00
|
|
|
constexpr uint32_t FRAME_INPLACE_BUFFER_INITIAL_SIZE = 128 * 1024;
|
2023-01-10 12:22:20 -08:00
|
|
|
|
|
|
|
|
struct SBaseImageState
|
|
|
|
|
{
|
|
|
|
|
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
VkAccessFlags accessMask = 0;
|
|
|
|
|
VkPipelineStageFlags stageMask = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SBaseImageState GetBaseImageState(CTexture* texture)
|
|
|
|
|
{
|
|
|
|
|
if (texture->GetUsage() & ITexture::Usage::SAMPLED)
|
|
|
|
|
{
|
|
|
|
|
return {
|
|
|
|
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
|
|
|
VK_ACCESS_SHADER_READ_BIT,
|
2024-01-17 11:40:27 -08:00
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT};
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
else if (texture->GetUsage() & ITexture::Usage::COLOR_ATTACHMENT)
|
|
|
|
|
{
|
|
|
|
|
return {
|
|
|
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
|
|
|
|
}
|
|
|
|
|
else if (texture->GetUsage() & ITexture::Usage::DEPTH_STENCIL_ATTACHMENT)
|
|
|
|
|
{
|
|
|
|
|
return {
|
|
|
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
|
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT};
|
|
|
|
|
}
|
2023-02-17 09:36:10 -08:00
|
|
|
// The only TRANSFER_DST usage means we can do only readbacks.
|
|
|
|
|
else if (texture->GetUsage() == ITexture::Usage::TRANSFER_DST)
|
|
|
|
|
{
|
|
|
|
|
return {
|
|
|
|
|
VK_IMAGE_LAYOUT_GENERAL,
|
|
|
|
|
VK_ACCESS_HOST_READ_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_HOST_BIT};
|
|
|
|
|
}
|
2023-01-10 12:22:20 -08:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ScopedImageLayoutTransition
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ScopedImageLayoutTransition(
|
|
|
|
|
CRingCommandContext& commandContext, const PS::span<CTexture* const> textures,
|
|
|
|
|
const VkImageLayout layout, const VkAccessFlags accessMask, const VkPipelineStageFlags stageMask)
|
|
|
|
|
: m_CommandContext(commandContext), m_Textures(textures), m_Layout(layout),
|
|
|
|
|
m_AccessMask(accessMask), m_StageMask(stageMask)
|
|
|
|
|
{
|
|
|
|
|
for (CTexture* const texture : m_Textures)
|
|
|
|
|
{
|
|
|
|
|
const auto state = GetBaseImageState(texture);
|
|
|
|
|
|
|
|
|
|
VkImageLayout oldLayout = state.layout;
|
|
|
|
|
if (!texture->IsInitialized())
|
|
|
|
|
oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
|
|
|
|
|
Utilities::SetTextureLayout(
|
|
|
|
|
m_CommandContext.GetCommandBuffer(), texture,
|
|
|
|
|
oldLayout, m_Layout,
|
|
|
|
|
state.accessMask, m_AccessMask, state.stageMask, m_StageMask);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~ScopedImageLayoutTransition()
|
|
|
|
|
{
|
|
|
|
|
for (CTexture* const texture : m_Textures)
|
|
|
|
|
{
|
|
|
|
|
const auto state = GetBaseImageState(texture);
|
|
|
|
|
|
|
|
|
|
Utilities::SetTextureLayout(
|
|
|
|
|
m_CommandContext.GetCommandBuffer(), texture,
|
|
|
|
|
m_Layout, state.layout,
|
|
|
|
|
m_AccessMask, state.accessMask, m_StageMask, state.stageMask);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
CRingCommandContext& m_CommandContext;
|
|
|
|
|
const PS::span<CTexture* const> m_Textures;
|
|
|
|
|
const VkImageLayout m_Layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
const VkAccessFlags m_AccessMask = 0;
|
|
|
|
|
const VkPipelineStageFlags m_StageMask = 0;
|
|
|
|
|
};
|
|
|
|
|
|
2023-02-17 09:36:10 -08:00
|
|
|
template<typename TransferOp>
|
|
|
|
|
void TransferForEachFramebufferAttachmentPair(
|
|
|
|
|
CRingCommandContext& commandContext,
|
|
|
|
|
CFramebuffer* sourceFramebuffer, CFramebuffer* destinationFramebuffer,
|
|
|
|
|
TransferOp transferOp)
|
|
|
|
|
{
|
|
|
|
|
const auto& sourceColorAttachments =
|
|
|
|
|
sourceFramebuffer->GetColorAttachments();
|
|
|
|
|
const auto& destinationColorAttachments =
|
|
|
|
|
destinationFramebuffer->GetColorAttachments();
|
|
|
|
|
ENSURE(sourceColorAttachments.size() == destinationColorAttachments.size());
|
|
|
|
|
|
|
|
|
|
for (CTexture* sourceColorAttachment : sourceColorAttachments)
|
|
|
|
|
ENSURE(sourceColorAttachment->GetUsage() & ITexture::Usage::TRANSFER_SRC);
|
|
|
|
|
for (CTexture* destinationColorAttachment : destinationColorAttachments)
|
|
|
|
|
ENSURE(destinationColorAttachment->GetUsage() & ITexture::Usage::TRANSFER_DST);
|
|
|
|
|
|
|
|
|
|
// TODO: combine barriers, reduce duplication, add depth.
|
|
|
|
|
ScopedImageLayoutTransition scopedColorAttachmentsTransition{
|
|
|
|
|
commandContext,
|
|
|
|
|
{sourceColorAttachments.begin(), sourceColorAttachments.end()},
|
|
|
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
|
|
|
VK_ACCESS_TRANSFER_READ_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT};
|
|
|
|
|
ScopedImageLayoutTransition destinationColorAttachmentsTransition{
|
|
|
|
|
commandContext,
|
|
|
|
|
{destinationColorAttachments.begin(), destinationColorAttachments.end()},
|
|
|
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
|
|
|
VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT};
|
|
|
|
|
|
|
|
|
|
for (CFramebuffer::ColorAttachments::size_type index = 0; index < destinationColorAttachments.size(); ++index)
|
|
|
|
|
{
|
|
|
|
|
CTexture* sourceColorAttachment = sourceColorAttachments[index];
|
|
|
|
|
CTexture* destinationColorAttachment = destinationColorAttachments[index];
|
|
|
|
|
|
|
|
|
|
transferOp(commandContext.GetCommandBuffer(), sourceColorAttachment, destinationColorAttachment);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sourceFramebuffer->GetDepthStencilAttachment() && destinationFramebuffer->GetDepthStencilAttachment())
|
|
|
|
|
{
|
|
|
|
|
transferOp(
|
|
|
|
|
commandContext.GetCommandBuffer(),
|
|
|
|
|
sourceFramebuffer->GetDepthStencilAttachment(),
|
|
|
|
|
destinationFramebuffer->GetDepthStencilAttachment());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 12:22:20 -08:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2023-02-01 14:09:25 -08:00
|
|
|
// A helper class to store consequent uploads to avoid many copy functions.
|
|
|
|
|
// We use a buffer in the device memory and NUMBER_OF_FRAMES_IN_FLIGHT
|
|
|
|
|
// times bigger buffer in the host memory.
|
|
|
|
|
class CDeviceCommandContext::CUploadRing
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CUploadRing(
|
|
|
|
|
CDevice* device, const IBuffer::Type type, const uint32_t initialCapacity);
|
|
|
|
|
|
|
|
|
|
CBuffer* GetBuffer() { return m_Buffer.get(); }
|
|
|
|
|
|
|
|
|
|
uint32_t ScheduleUpload(
|
|
|
|
|
VkCommandBuffer commandBuffer, const PS::span<const std::byte> data,
|
|
|
|
|
const uint32_t alignment);
|
|
|
|
|
|
|
|
|
|
void ExecuteUploads(VkCommandBuffer commandBuffer);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void ResizeIfNeeded(
|
|
|
|
|
VkCommandBuffer commandBuffer, const uint32_t dataSize);
|
|
|
|
|
|
|
|
|
|
CDevice* m_Device = nullptr;
|
|
|
|
|
IBuffer::Type m_Type = IBuffer::Type::VERTEX;
|
|
|
|
|
uint32_t m_Capacity = 0;
|
|
|
|
|
uint32_t m_BlockIndex = 0, m_BlockOffset = 0;
|
|
|
|
|
std::unique_ptr<CBuffer> m_Buffer, m_StagingBuffer;
|
|
|
|
|
std::byte* m_StagingBufferMappedData = nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
CDeviceCommandContext::CUploadRing::CUploadRing(
|
|
|
|
|
CDevice* device, const IBuffer::Type type, const uint32_t initialCapacity)
|
|
|
|
|
: m_Device(device), m_Type(type)
|
|
|
|
|
{
|
|
|
|
|
ResizeIfNeeded(VK_NULL_HANDLE, initialCapacity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::CUploadRing::ResizeIfNeeded(
|
|
|
|
|
VkCommandBuffer commandBuffer, const uint32_t dataSize)
|
|
|
|
|
{
|
2024-10-30 12:26:43 -07:00
|
|
|
// We need to pad the data size for uniforms because we use dynamic offsets
|
|
|
|
|
// with a fixed range.
|
|
|
|
|
const uint32_t paddedDataSize{
|
|
|
|
|
m_Type == IBuffer::Type::UNIFORM ? round_up_to_pow2(dataSize) : dataSize};
|
|
|
|
|
const bool resizeNeeded = !m_Buffer || m_BlockOffset + paddedDataSize > m_Capacity;
|
2023-02-01 14:09:25 -08:00
|
|
|
if (!resizeNeeded)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (m_Buffer && m_BlockOffset > 0)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(commandBuffer != VK_NULL_HANDLE);
|
|
|
|
|
ExecuteUploads(commandBuffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_Capacity = std::max(m_Capacity * 2, round_up_to_pow2(dataSize));
|
|
|
|
|
|
|
|
|
|
m_Buffer = m_Device->CreateCBuffer(
|
2024-06-10 12:31:41 -07:00
|
|
|
"UploadRingBuffer", m_Type, m_Capacity, IBuffer::Usage::DYNAMIC | IBuffer::Usage::TRANSFER_DST);
|
2023-02-01 14:09:25 -08:00
|
|
|
m_StagingBuffer = m_Device->CreateCBuffer(
|
2024-06-10 12:31:41 -07:00
|
|
|
"UploadRingStagingBuffer", IBuffer::Type::UPLOAD, NUMBER_OF_FRAMES_IN_FLIGHT * m_Capacity, IBuffer::Usage::DYNAMIC | IBuffer::Usage::TRANSFER_SRC);
|
2023-02-01 14:09:25 -08:00
|
|
|
ENSURE(m_Buffer && m_StagingBuffer);
|
|
|
|
|
m_StagingBufferMappedData = static_cast<std::byte*>(m_StagingBuffer->GetMappedData());
|
|
|
|
|
ENSURE(m_StagingBufferMappedData);
|
|
|
|
|
|
|
|
|
|
m_BlockIndex = 0;
|
|
|
|
|
m_BlockOffset = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t CDeviceCommandContext::CUploadRing::ScheduleUpload(
|
|
|
|
|
VkCommandBuffer commandBuffer, const PS::span<const std::byte> data,
|
|
|
|
|
const uint32_t alignment)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(data.size() > 0);
|
|
|
|
|
ENSURE(is_pow2(alignment));
|
|
|
|
|
|
|
|
|
|
m_BlockOffset = (m_BlockOffset + alignment - 1) & ~(alignment - 1);
|
|
|
|
|
|
|
|
|
|
ResizeIfNeeded(commandBuffer, data.size());
|
|
|
|
|
|
|
|
|
|
const uint32_t destination = m_BlockIndex * m_Capacity + m_BlockOffset;
|
|
|
|
|
const uint32_t offset = m_BlockOffset;
|
|
|
|
|
m_BlockOffset += data.size();
|
|
|
|
|
std::memcpy(m_StagingBufferMappedData + destination, data.data(), data.size());
|
|
|
|
|
return offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::CUploadRing::ExecuteUploads(
|
|
|
|
|
VkCommandBuffer commandBuffer)
|
|
|
|
|
{
|
|
|
|
|
if (m_BlockOffset == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2023-06-05 09:32:18 -07:00
|
|
|
const VkPipelineStageFlags stageMask =
|
|
|
|
|
m_Type == IBuffer::Type::UNIFORM
|
2024-01-17 11:40:27 -08:00
|
|
|
? VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT
|
2023-06-05 09:32:18 -07:00
|
|
|
: VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
|
|
|
|
|
|
|
|
|
Utilities::SubmitBufferMemoryBarrier(
|
|
|
|
|
commandBuffer, m_Buffer.get(), 0, VK_WHOLE_SIZE,
|
|
|
|
|
VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
|
|
|
stageMask, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
|
|
|
|
|
2023-02-01 14:09:25 -08:00
|
|
|
VkBufferCopy region{};
|
|
|
|
|
region.srcOffset = m_BlockIndex * m_Capacity;
|
|
|
|
|
region.dstOffset = 0;
|
|
|
|
|
region.size = m_BlockOffset;
|
|
|
|
|
|
|
|
|
|
vkCmdCopyBuffer(
|
|
|
|
|
commandBuffer,
|
|
|
|
|
m_StagingBuffer->GetVkBuffer(), m_Buffer->GetVkBuffer(),
|
|
|
|
|
1, ®ion);
|
|
|
|
|
|
2023-06-05 09:32:18 -07:00
|
|
|
Utilities::SubmitBufferMemoryBarrier(
|
|
|
|
|
commandBuffer, m_Buffer.get(), 0, VK_WHOLE_SIZE,
|
|
|
|
|
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, stageMask);
|
2023-02-01 14:09:25 -08:00
|
|
|
|
|
|
|
|
m_BlockIndex = (m_BlockIndex + 1) % NUMBER_OF_FRAMES_IN_FLIGHT;
|
|
|
|
|
m_BlockOffset = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 12:22:20 -08:00
|
|
|
// static
|
|
|
|
|
std::unique_ptr<IDeviceCommandContext> CDeviceCommandContext::Create(CDevice* device)
|
|
|
|
|
{
|
|
|
|
|
std::unique_ptr<CDeviceCommandContext> deviceCommandContext(new CDeviceCommandContext());
|
|
|
|
|
deviceCommandContext->m_Device = device;
|
|
|
|
|
deviceCommandContext->m_DebugScopedLabels = device->GetCapabilities().debugScopedLabels;
|
|
|
|
|
deviceCommandContext->m_PrependCommandContext =
|
|
|
|
|
device->CreateRingCommandContext(NUMBER_OF_FRAMES_IN_FLIGHT);
|
2024-12-05 09:32:15 -08:00
|
|
|
ENSURE(deviceCommandContext->m_PrependCommandContext);
|
2023-01-10 12:22:20 -08:00
|
|
|
deviceCommandContext->m_CommandContext =
|
|
|
|
|
device->CreateRingCommandContext(NUMBER_OF_FRAMES_IN_FLIGHT);
|
2024-12-05 09:32:15 -08:00
|
|
|
ENSURE(deviceCommandContext->m_CommandContext);
|
2023-01-10 12:22:20 -08:00
|
|
|
|
2023-02-01 14:09:25 -08:00
|
|
|
deviceCommandContext->m_VertexUploadRing = std::make_unique<CUploadRing>(
|
|
|
|
|
device, IBuffer::Type::VERTEX, FRAME_INPLACE_BUFFER_INITIAL_SIZE);
|
|
|
|
|
deviceCommandContext->m_IndexUploadRing = std::make_unique<CUploadRing>(
|
|
|
|
|
device, IBuffer::Type::INDEX, FRAME_INPLACE_BUFFER_INITIAL_SIZE);
|
|
|
|
|
deviceCommandContext->m_UniformUploadRing = std::make_unique<CUploadRing>(
|
|
|
|
|
device, IBuffer::Type::UNIFORM, UNIFORM_BUFFER_INITIAL_SIZE);
|
2023-01-10 12:22:20 -08:00
|
|
|
|
2025-01-29 07:38:52 -08:00
|
|
|
deviceCommandContext->m_DebugBarrierAfterFramebufferPass = g_ConfigDB.Get(
|
2023-06-05 09:32:18 -07:00
|
|
|
"renderer.backend.vulkan.debugbarrierafterframebufferpass",
|
|
|
|
|
deviceCommandContext->m_DebugBarrierAfterFramebufferPass);
|
|
|
|
|
|
2023-01-10 12:22:20 -08:00
|
|
|
return deviceCommandContext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CDeviceCommandContext::CDeviceCommandContext() = default;
|
|
|
|
|
|
|
|
|
|
CDeviceCommandContext::~CDeviceCommandContext()
|
|
|
|
|
{
|
|
|
|
|
VkDevice device = m_Device->GetVkDevice();
|
|
|
|
|
|
|
|
|
|
vkDeviceWaitIdle(device);
|
|
|
|
|
|
|
|
|
|
if (m_UniformDescriptorPool != VK_NULL_HANDLE)
|
|
|
|
|
vkDestroyDescriptorPool(device, m_UniformDescriptorPool, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IDevice* CDeviceCommandContext::GetDevice()
|
|
|
|
|
{
|
|
|
|
|
return m_Device;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetGraphicsPipelineState(
|
|
|
|
|
IGraphicsPipelineState* pipelineState)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(pipelineState);
|
2024-10-19 13:53:36 -07:00
|
|
|
if (m_GraphicsPipelineState && m_GraphicsPipelineState->GetUID() == pipelineState->As<CGraphicsPipelineState>()->GetUID())
|
|
|
|
|
return;
|
2023-01-10 12:22:20 -08:00
|
|
|
m_GraphicsPipelineState = pipelineState->As<CGraphicsPipelineState>();
|
|
|
|
|
|
|
|
|
|
CShaderProgram* shaderProgram = m_GraphicsPipelineState->GetShaderProgram()->As<CShaderProgram>();
|
|
|
|
|
if (m_ShaderProgram != shaderProgram)
|
|
|
|
|
{
|
|
|
|
|
if (m_ShaderProgram)
|
|
|
|
|
m_ShaderProgram->Unbind();
|
|
|
|
|
m_ShaderProgram = shaderProgram;
|
|
|
|
|
}
|
|
|
|
|
m_IsPipelineStateDirty = true;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 11:40:27 -08:00
|
|
|
void CDeviceCommandContext::SetComputePipelineState(
|
|
|
|
|
IComputePipelineState* pipelineState)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(pipelineState);
|
2024-10-19 13:53:36 -07:00
|
|
|
if (m_ComputePipelineState && m_ComputePipelineState->GetUID() == pipelineState->As<CComputePipelineState>()->GetUID())
|
|
|
|
|
return;
|
2024-10-07 11:16:51 -07:00
|
|
|
m_ComputePipelineState = pipelineState->As<CComputePipelineState>();
|
2024-01-17 11:40:27 -08:00
|
|
|
|
2024-10-07 11:16:51 -07:00
|
|
|
CShaderProgram* shaderProgram = m_ComputePipelineState->GetShaderProgram()->As<CShaderProgram>();
|
|
|
|
|
if (m_ShaderProgram != shaderProgram)
|
2024-01-17 11:40:27 -08:00
|
|
|
{
|
2024-10-07 11:16:51 -07:00
|
|
|
if (m_ShaderProgram)
|
|
|
|
|
m_ShaderProgram->Unbind();
|
|
|
|
|
m_ShaderProgram = shaderProgram;
|
2024-01-17 11:40:27 -08:00
|
|
|
}
|
2024-10-07 11:16:51 -07:00
|
|
|
m_IsPipelineStateDirty = true;
|
2024-01-17 11:40:27 -08:00
|
|
|
}
|
|
|
|
|
|
2023-02-17 09:36:10 -08:00
|
|
|
void CDeviceCommandContext::BlitFramebuffer(
|
|
|
|
|
IFramebuffer* sourceFramebuffer, IFramebuffer* destinationFramebuffer,
|
|
|
|
|
const Rect& sourceRegion, const Rect& destinationRegion,
|
|
|
|
|
const Sampler::Filter filter)
|
2023-01-10 12:22:20 -08:00
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
2023-02-17 09:36:10 -08:00
|
|
|
ENSURE(sourceRegion.x >= 0 && sourceRegion.x + sourceRegion.width <= static_cast<int32_t>(sourceFramebuffer->GetWidth()));
|
|
|
|
|
ENSURE(sourceRegion.y >= 0 && sourceRegion.y + sourceRegion.height <= static_cast<int32_t>(sourceFramebuffer->GetHeight()));
|
|
|
|
|
ENSURE(destinationRegion.x >= 0 && destinationRegion.x + destinationRegion.width <= static_cast<int32_t>(destinationFramebuffer->GetWidth()));
|
|
|
|
|
ENSURE(destinationRegion.y >= 0 && destinationRegion.y + destinationRegion.height <= static_cast<int32_t>(destinationFramebuffer->GetHeight()));
|
2023-01-10 12:22:20 -08:00
|
|
|
|
2023-02-17 09:36:10 -08:00
|
|
|
TransferForEachFramebufferAttachmentPair(
|
2023-01-10 12:22:20 -08:00
|
|
|
*m_CommandContext,
|
2023-02-17 09:36:10 -08:00
|
|
|
sourceFramebuffer->As<CFramebuffer>(), destinationFramebuffer->As<CFramebuffer>(),
|
|
|
|
|
[&sourceRegion, &destinationRegion, filter](
|
|
|
|
|
VkCommandBuffer commandBuffer, CTexture* sourceColorAttachment, CTexture* destinationColorAttachment)
|
2023-01-10 12:22:20 -08:00
|
|
|
{
|
2023-02-17 09:36:10 -08:00
|
|
|
// TODO: we need to check for VK_FORMAT_FEATURE_BLIT_*_BIT for used formats.
|
|
|
|
|
|
|
|
|
|
const bool isDepth = IsDepthFormat(sourceColorAttachment->GetFormat());
|
|
|
|
|
const VkImageAspectFlags aspectMask = isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
2023-01-10 12:22:20 -08:00
|
|
|
|
|
|
|
|
VkImageBlit region{};
|
2023-02-17 09:36:10 -08:00
|
|
|
// Currently (0, 0) is the left-bottom corner (legacy from GL), so
|
|
|
|
|
// we need to adjust the regions.
|
|
|
|
|
const uint32_t sourceHeight = sourceColorAttachment->GetHeight();
|
|
|
|
|
const uint32_t destinationHeight = destinationColorAttachment->GetHeight();
|
|
|
|
|
region.srcOffsets[0].x = sourceRegion.x;
|
|
|
|
|
region.srcOffsets[0].y = sourceHeight - sourceRegion.y - sourceRegion.height;
|
|
|
|
|
region.dstOffsets[0].x = destinationRegion.x;
|
|
|
|
|
region.dstOffsets[0].y = destinationHeight - destinationRegion.y - destinationRegion.height;
|
|
|
|
|
region.srcOffsets[1].x = sourceRegion.x + sourceRegion.width;
|
|
|
|
|
region.srcOffsets[1].y = sourceHeight - sourceRegion.y;
|
2023-01-10 12:22:20 -08:00
|
|
|
region.srcOffsets[1].z = 1;
|
2023-02-17 09:36:10 -08:00
|
|
|
region.dstOffsets[1].x = destinationRegion.x + destinationRegion.width;
|
|
|
|
|
region.dstOffsets[1].y = destinationHeight - destinationRegion.y;
|
2023-01-10 12:22:20 -08:00
|
|
|
region.dstOffsets[1].z = 1;
|
2023-02-17 09:36:10 -08:00
|
|
|
region.srcSubresource.aspectMask = aspectMask;
|
2023-01-10 12:22:20 -08:00
|
|
|
region.srcSubresource.mipLevel = 0;
|
|
|
|
|
region.srcSubresource.baseArrayLayer = 0;
|
|
|
|
|
region.srcSubresource.layerCount = 1;
|
2023-02-17 09:36:10 -08:00
|
|
|
region.dstSubresource.aspectMask = aspectMask;
|
2023-01-10 12:22:20 -08:00
|
|
|
region.dstSubresource.mipLevel = 0;
|
|
|
|
|
region.dstSubresource.baseArrayLayer = 0;
|
|
|
|
|
region.dstSubresource.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
vkCmdBlitImage(
|
2023-02-17 09:36:10 -08:00
|
|
|
commandBuffer,
|
2023-01-10 12:22:20 -08:00
|
|
|
sourceColorAttachment->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
|
|
|
destinationColorAttachment->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
2023-02-17 09:36:10 -08:00
|
|
|
1, ®ion, filter == Sampler::Filter::LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::ResolveFramebuffer(
|
|
|
|
|
IFramebuffer* sourceFramebuffer, IFramebuffer* destinationFramebuffer)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
ENSURE(sourceFramebuffer->As<CFramebuffer>()->GetSampleCount() > 1);
|
|
|
|
|
ENSURE(destinationFramebuffer->As<CFramebuffer>()->GetSampleCount() == 1);
|
|
|
|
|
ENSURE(sourceFramebuffer->As<CFramebuffer>()->GetWidth() == destinationFramebuffer->As<CFramebuffer>()->GetWidth());
|
|
|
|
|
ENSURE(sourceFramebuffer->As<CFramebuffer>()->GetHeight() == destinationFramebuffer->As<CFramebuffer>()->GetHeight());
|
|
|
|
|
|
|
|
|
|
TransferForEachFramebufferAttachmentPair(
|
|
|
|
|
*m_CommandContext,
|
|
|
|
|
sourceFramebuffer->As<CFramebuffer>(), destinationFramebuffer->As<CFramebuffer>(),
|
|
|
|
|
[](VkCommandBuffer commandBuffer, CTexture* sourceColorAttachment, CTexture* destinationColorAttachment)
|
2023-01-10 12:22:20 -08:00
|
|
|
{
|
2023-02-17 09:36:10 -08:00
|
|
|
ENSURE(sourceColorAttachment->GetFormat() == destinationColorAttachment->GetFormat());
|
|
|
|
|
ENSURE(!IsDepthFormat(sourceColorAttachment->GetFormat()));
|
|
|
|
|
const VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
2023-01-10 12:22:20 -08:00
|
|
|
|
|
|
|
|
VkImageResolve region{};
|
|
|
|
|
region.extent.width = sourceColorAttachment->GetWidth();
|
|
|
|
|
region.extent.height = sourceColorAttachment->GetHeight();
|
|
|
|
|
region.extent.depth = 1;
|
2023-02-17 09:36:10 -08:00
|
|
|
region.srcSubresource.aspectMask = aspectMask;
|
2023-01-10 12:22:20 -08:00
|
|
|
region.srcSubresource.mipLevel = 0;
|
|
|
|
|
region.srcSubresource.baseArrayLayer = 0;
|
|
|
|
|
region.srcSubresource.layerCount = 1;
|
2023-02-17 09:36:10 -08:00
|
|
|
region.dstSubresource.aspectMask = aspectMask;
|
2023-01-10 12:22:20 -08:00
|
|
|
region.dstSubresource.mipLevel = 0;
|
|
|
|
|
region.dstSubresource.baseArrayLayer = 0;
|
|
|
|
|
region.dstSubresource.layerCount = 1;
|
|
|
|
|
|
|
|
|
|
vkCmdResolveImage(
|
2023-02-17 09:36:10 -08:00
|
|
|
commandBuffer,
|
2023-01-10 12:22:20 -08:00
|
|
|
sourceColorAttachment->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
|
|
|
destinationColorAttachment->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
|
|
|
1, ®ion);
|
2023-02-17 09:36:10 -08:00
|
|
|
});
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::ClearFramebuffer(const bool color, const bool depth, const bool stencil)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsideFramebufferPass);
|
|
|
|
|
ENSURE(m_Framebuffer);
|
|
|
|
|
PS::StaticVector<VkClearAttachment, 4> clearAttachments;
|
|
|
|
|
if (color)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_Framebuffer->GetColorAttachments().empty());
|
|
|
|
|
for (size_t index = 0; index < m_Framebuffer->GetColorAttachments().size(); ++index)
|
|
|
|
|
{
|
|
|
|
|
VkClearAttachment clearAttachment{};
|
|
|
|
|
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
const CColor& clearColor = m_Framebuffer->GetClearColor();
|
|
|
|
|
clearAttachment.clearValue.color.float32[0] = clearColor.r;
|
|
|
|
|
clearAttachment.clearValue.color.float32[1] = clearColor.g;
|
|
|
|
|
clearAttachment.clearValue.color.float32[2] = clearColor.b;
|
|
|
|
|
clearAttachment.clearValue.color.float32[3] = clearColor.a;
|
|
|
|
|
clearAttachment.colorAttachment = index;
|
|
|
|
|
clearAttachments.emplace_back(std::move(clearAttachment));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (depth || stencil)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_Framebuffer->GetDepthStencilAttachment());
|
|
|
|
|
if (stencil)
|
2023-01-11 22:32:52 -08:00
|
|
|
{
|
|
|
|
|
const Format depthStencilFormat =
|
|
|
|
|
m_Framebuffer->GetDepthStencilAttachment()->GetFormat();
|
|
|
|
|
ENSURE(depthStencilFormat == Format::D24_UNORM_S8_UINT ||
|
|
|
|
|
depthStencilFormat == Format::D32_SFLOAT_S8_UINT);
|
|
|
|
|
}
|
2023-01-10 12:22:20 -08:00
|
|
|
VkClearAttachment clearAttachment{};
|
|
|
|
|
if (depth)
|
|
|
|
|
clearAttachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
|
|
|
if (stencil)
|
|
|
|
|
clearAttachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
|
|
|
clearAttachment.clearValue.depthStencil.depth = 1.0f;
|
|
|
|
|
clearAttachment.clearValue.depthStencil.stencil = 0;
|
|
|
|
|
clearAttachments.emplace_back(std::move(clearAttachment));
|
|
|
|
|
}
|
|
|
|
|
VkClearRect clearRect{};
|
|
|
|
|
clearRect.layerCount = 1;
|
|
|
|
|
clearRect.rect.offset.x = 0;
|
|
|
|
|
clearRect.rect.offset.y = 0;
|
|
|
|
|
clearRect.rect.extent.width = m_Framebuffer->GetWidth();
|
|
|
|
|
clearRect.rect.extent.height = m_Framebuffer->GetHeight();
|
|
|
|
|
vkCmdClearAttachments(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(),
|
|
|
|
|
clearAttachments.size(), clearAttachments.data(),
|
|
|
|
|
1, &clearRect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::BeginFramebufferPass(IFramebuffer* framebuffer)
|
|
|
|
|
{
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
ENSURE(!m_InsideComputePass);
|
2023-01-10 12:22:20 -08:00
|
|
|
ENSURE(framebuffer);
|
|
|
|
|
m_IsPipelineStateDirty = true;
|
|
|
|
|
m_Framebuffer = framebuffer->As<CFramebuffer>();
|
|
|
|
|
m_GraphicsPipelineState = nullptr;
|
|
|
|
|
m_VertexInputLayout = nullptr;
|
|
|
|
|
|
|
|
|
|
SetScissors(0, nullptr);
|
|
|
|
|
|
|
|
|
|
for (CTexture* colorAttachment : m_Framebuffer->GetColorAttachments())
|
|
|
|
|
{
|
|
|
|
|
if (!(colorAttachment->GetUsage() & ITexture::Usage::SAMPLED) && colorAttachment->IsInitialized())
|
|
|
|
|
continue;
|
|
|
|
|
VkImageLayout oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
|
if (!colorAttachment->IsInitialized())
|
|
|
|
|
oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
Utilities::SetTextureLayout(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), colorAttachment,
|
|
|
|
|
oldLayout,
|
|
|
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
|
|
|
VK_ACCESS_SHADER_READ_BIT,
|
|
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
2024-01-17 11:40:27 -08:00
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CTexture* depthStencilAttachment = m_Framebuffer->GetDepthStencilAttachment();
|
|
|
|
|
if (depthStencilAttachment && ((depthStencilAttachment->GetUsage() & ITexture::Usage::SAMPLED) || !depthStencilAttachment->IsInitialized()))
|
|
|
|
|
{
|
|
|
|
|
VkImageLayout oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
|
if (!depthStencilAttachment->IsInitialized())
|
|
|
|
|
oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
|
Utilities::SetTextureLayout(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), depthStencilAttachment, oldLayout,
|
|
|
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
|
|
|
VK_ACCESS_SHADER_READ_BIT,
|
|
|
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
2024-01-17 11:40:27 -08:00
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
2023-01-10 12:22:20 -08:00
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_InsideFramebufferPass = true;
|
|
|
|
|
|
|
|
|
|
VkRenderPassBeginInfo renderPassBeginInfo{};
|
|
|
|
|
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
|
|
|
renderPassBeginInfo.renderPass = m_Framebuffer->GetRenderPass();
|
|
|
|
|
renderPassBeginInfo.framebuffer = m_Framebuffer->GetFramebuffer();
|
|
|
|
|
renderPassBeginInfo.renderArea.offset = { 0, 0 };
|
|
|
|
|
renderPassBeginInfo.renderArea.extent = { m_Framebuffer->GetWidth(), m_Framebuffer->GetHeight() };
|
|
|
|
|
|
|
|
|
|
PS::StaticVector<VkClearValue, 4> clearValues;
|
|
|
|
|
const bool needsClearValues =
|
|
|
|
|
m_Framebuffer->GetColorAttachmentLoadOp() == AttachmentLoadOp::CLEAR ||
|
|
|
|
|
(m_Framebuffer->GetDepthStencilAttachment() &&
|
|
|
|
|
m_Framebuffer->GetDepthStencilAttachmentLoadOp() == AttachmentLoadOp::CLEAR);
|
|
|
|
|
if (needsClearValues)
|
|
|
|
|
{
|
|
|
|
|
for (CTexture* colorAttachment : m_Framebuffer->GetColorAttachments())
|
|
|
|
|
{
|
|
|
|
|
UNUSED2(colorAttachment);
|
|
|
|
|
const CColor& clearColor = m_Framebuffer->GetClearColor();
|
|
|
|
|
// The four array elements of the clear color map to R, G, B, and A
|
|
|
|
|
// components of image formats, in order.
|
|
|
|
|
clearValues.emplace_back();
|
|
|
|
|
clearValues.back().color.float32[0] = clearColor.r;
|
|
|
|
|
clearValues.back().color.float32[1] = clearColor.g;
|
|
|
|
|
clearValues.back().color.float32[2] = clearColor.b;
|
|
|
|
|
clearValues.back().color.float32[3] = clearColor.a;
|
|
|
|
|
}
|
|
|
|
|
if (m_Framebuffer->GetDepthStencilAttachment())
|
|
|
|
|
{
|
|
|
|
|
clearValues.emplace_back();
|
|
|
|
|
clearValues.back().depthStencil.depth = 1.0f;
|
|
|
|
|
clearValues.back().depthStencil.stencil = 0;
|
|
|
|
|
}
|
|
|
|
|
renderPassBeginInfo.clearValueCount = clearValues.size();
|
|
|
|
|
renderPassBeginInfo.pClearValues = clearValues.data();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vkCmdBeginRenderPass(m_CommandContext->GetCommandBuffer(), &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::EndFramebufferPass()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsideFramebufferPass);
|
|
|
|
|
vkCmdEndRenderPass(m_CommandContext->GetCommandBuffer());
|
|
|
|
|
|
|
|
|
|
m_InsideFramebufferPass = false;
|
|
|
|
|
m_BoundIndexBuffer = nullptr;
|
|
|
|
|
|
|
|
|
|
ENSURE(m_Framebuffer);
|
|
|
|
|
for (CTexture* colorAttachment : m_Framebuffer->GetColorAttachments())
|
|
|
|
|
{
|
|
|
|
|
if (!(colorAttachment->GetUsage() & ITexture::Usage::SAMPLED))
|
|
|
|
|
continue;
|
|
|
|
|
Utilities::SetTextureLayout(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), colorAttachment,
|
|
|
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
|
|
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
|
|
|
VK_ACCESS_SHADER_READ_BIT,
|
2024-01-17 11:40:27 -08:00
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CTexture* depthStencilAttachment = m_Framebuffer->GetDepthStencilAttachment();
|
|
|
|
|
if (depthStencilAttachment && (depthStencilAttachment->GetUsage() & ITexture::Usage::SAMPLED))
|
|
|
|
|
{
|
|
|
|
|
Utilities::SetTextureLayout(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), depthStencilAttachment,
|
|
|
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
|
|
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
|
|
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
|
|
|
VK_ACCESS_SHADER_READ_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
2024-01-17 11:40:27 -08:00
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_LastBoundPipeline = VK_NULL_HANDLE;
|
|
|
|
|
if (m_ShaderProgram)
|
|
|
|
|
m_ShaderProgram->Unbind();
|
|
|
|
|
m_ShaderProgram = nullptr;
|
2024-10-07 11:16:51 -07:00
|
|
|
m_GraphicsPipelineState = nullptr;
|
2023-06-05 09:32:18 -07:00
|
|
|
|
|
|
|
|
if (m_DebugBarrierAfterFramebufferPass)
|
|
|
|
|
Utilities::SubmitDebugSyncMemoryBarrier(m_CommandContext->GetCommandBuffer());
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::ReadbackFramebufferSync(
|
|
|
|
|
const uint32_t x, const uint32_t y, const uint32_t width, const uint32_t height,
|
|
|
|
|
void* data)
|
|
|
|
|
{
|
2023-02-17 09:36:10 -08:00
|
|
|
CTexture* texture = m_Device->GetCurrentBackbufferTexture();
|
|
|
|
|
if (!texture)
|
|
|
|
|
{
|
|
|
|
|
LOGERROR("Vulkan: backbuffer is unavailable.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!(texture->GetUsage() & ITexture::Usage::TRANSFER_SRC))
|
|
|
|
|
{
|
|
|
|
|
LOGERROR("Vulkan: backbuffer doesn't support readback.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_QueuedReadbacks.emplace_back(x, y, width, height, data);
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UploadTexture(ITexture* texture, const Format dataFormat,
|
|
|
|
|
const void* data, const size_t dataSize,
|
|
|
|
|
const uint32_t level, const uint32_t layer)
|
|
|
|
|
{
|
|
|
|
|
(m_InsideFramebufferPass ? m_PrependCommandContext : m_CommandContext)->ScheduleUpload(
|
|
|
|
|
texture->As<CTexture>(), dataFormat, data, dataSize, level, layer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UploadTextureRegion(ITexture* texture, const Format dataFormat,
|
|
|
|
|
const void* data, const size_t dataSize,
|
|
|
|
|
const uint32_t xOffset, const uint32_t yOffset,
|
|
|
|
|
const uint32_t width, const uint32_t height,
|
|
|
|
|
const uint32_t level, const uint32_t layer)
|
|
|
|
|
{
|
|
|
|
|
(m_InsideFramebufferPass ? m_PrependCommandContext : m_CommandContext)->ScheduleUpload(
|
|
|
|
|
texture->As<CTexture>(), dataFormat, data, dataSize, xOffset, yOffset, width, height, level, layer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UploadBuffer(IBuffer* buffer, const void* data, const uint32_t dataSize)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
m_CommandContext->ScheduleUpload(
|
|
|
|
|
buffer->As<CBuffer>(), data, 0, dataSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UploadBuffer(IBuffer* buffer, const UploadBufferFunction& uploadFunction)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
m_CommandContext->ScheduleUpload(
|
|
|
|
|
buffer->As<CBuffer>(), 0, buffer->As<CBuffer>()->GetSize(), uploadFunction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UploadBufferRegion(
|
|
|
|
|
IBuffer* buffer, const void* data, const uint32_t dataOffset, const uint32_t dataSize)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
m_CommandContext->ScheduleUpload(
|
|
|
|
|
buffer->As<CBuffer>(), data, dataOffset, dataSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UploadBufferRegion(
|
|
|
|
|
IBuffer* buffer, const uint32_t dataOffset, const uint32_t dataSize,
|
|
|
|
|
const UploadBufferFunction& uploadFunction)
|
|
|
|
|
{
|
|
|
|
|
m_CommandContext->ScheduleUpload(
|
|
|
|
|
buffer->As<CBuffer>(), dataOffset, dataSize, uploadFunction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetScissors(const uint32_t scissorCount, const Rect* scissors)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_Framebuffer);
|
|
|
|
|
ENSURE(scissorCount <= 1);
|
|
|
|
|
VkRect2D scissor{};
|
|
|
|
|
if (scissorCount == 1)
|
|
|
|
|
{
|
|
|
|
|
// the x and y members of offset member of any element of pScissors must be
|
|
|
|
|
// greater than or equal to 0.
|
|
|
|
|
int32_t x = scissors[0].x;
|
|
|
|
|
int32_t y = m_Framebuffer->GetHeight() - scissors[0].y - scissors[0].height;
|
|
|
|
|
int32_t width = scissors[0].width;
|
|
|
|
|
int32_t height = scissors[0].height;
|
|
|
|
|
if (x < 0)
|
|
|
|
|
{
|
|
|
|
|
width = std::max(0, width + x);
|
|
|
|
|
x = 0;
|
|
|
|
|
}
|
|
|
|
|
if (y < 0)
|
|
|
|
|
{
|
|
|
|
|
height = std::max(0, height + y);
|
|
|
|
|
y = 0;
|
|
|
|
|
}
|
|
|
|
|
scissor.offset.x = x;
|
|
|
|
|
scissor.offset.y = y;
|
|
|
|
|
scissor.extent.width = width;
|
|
|
|
|
scissor.extent.height = height;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
scissor.extent.width = m_Framebuffer->GetWidth();
|
|
|
|
|
scissor.extent.height = m_Framebuffer->GetHeight();
|
|
|
|
|
}
|
|
|
|
|
vkCmdSetScissor(m_CommandContext->GetCommandBuffer(), 0, 1, &scissor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetViewports(const uint32_t viewportCount, const Rect* viewports)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_Framebuffer);
|
|
|
|
|
ENSURE(viewportCount == 1);
|
|
|
|
|
|
|
|
|
|
VkViewport viewport{};
|
|
|
|
|
viewport.minDepth = 0.0f;
|
|
|
|
|
viewport.maxDepth = 1.0f;
|
|
|
|
|
viewport.x = static_cast<float>(viewports[0].x);
|
|
|
|
|
viewport.y = static_cast<float>(static_cast<int32_t>(m_Framebuffer->GetHeight()) - viewports[0].y - viewports[0].height);
|
|
|
|
|
viewport.width = static_cast<float>(viewports[0].width);
|
|
|
|
|
viewport.height = static_cast<float>(viewports[0].height);
|
|
|
|
|
|
|
|
|
|
vkCmdSetViewport(m_CommandContext->GetCommandBuffer(), 0, 1, &viewport);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetVertexInputLayout(
|
|
|
|
|
IVertexInputLayout* vertexInputLayout)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(vertexInputLayout);
|
2024-01-17 11:55:28 -08:00
|
|
|
if (m_VertexInputLayout != vertexInputLayout->As<CVertexInputLayout>())
|
|
|
|
|
{
|
|
|
|
|
m_IsPipelineStateDirty = true;
|
|
|
|
|
m_VertexInputLayout = vertexInputLayout->As<CVertexInputLayout>();
|
|
|
|
|
}
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetVertexBuffer(
|
|
|
|
|
const uint32_t bindingSlot, IBuffer* buffer, const uint32_t offset)
|
|
|
|
|
{
|
|
|
|
|
BindVertexBuffer(bindingSlot, buffer->As<CBuffer>(), offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetVertexBufferData(
|
|
|
|
|
const uint32_t bindingSlot, const void* data, const uint32_t dataSize)
|
|
|
|
|
{
|
|
|
|
|
// TODO: check vertex buffer alignment.
|
|
|
|
|
const uint32_t ALIGNMENT = 32;
|
|
|
|
|
|
2023-02-01 14:09:25 -08:00
|
|
|
const uint32_t offset = m_VertexUploadRing->ScheduleUpload(
|
|
|
|
|
m_PrependCommandContext->GetCommandBuffer(),
|
|
|
|
|
PS::span<const std::byte>{static_cast<const std::byte*>(data), dataSize}, ALIGNMENT);
|
|
|
|
|
BindVertexBuffer(bindingSlot, m_VertexUploadRing->GetBuffer(), offset);
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetIndexBuffer(IBuffer* buffer)
|
|
|
|
|
{
|
|
|
|
|
BindIndexBuffer(buffer->As<CBuffer>(), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetIndexBufferData(
|
|
|
|
|
const void* data, const uint32_t dataSize)
|
|
|
|
|
{
|
|
|
|
|
// TODO: check index buffer alignment.
|
|
|
|
|
const uint32_t ALIGNMENT = 32;
|
|
|
|
|
|
2023-02-01 14:09:25 -08:00
|
|
|
const uint32_t offset = m_IndexUploadRing->ScheduleUpload(
|
|
|
|
|
m_PrependCommandContext->GetCommandBuffer(),
|
|
|
|
|
PS::span<const std::byte>{static_cast<const std::byte*>(data), dataSize}, ALIGNMENT);
|
|
|
|
|
BindIndexBuffer(m_IndexUploadRing->GetBuffer(), offset);
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::BeginPass()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsideFramebufferPass);
|
|
|
|
|
m_InsidePass = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::EndPass()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsidePass);
|
|
|
|
|
m_InsidePass = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::Draw(const uint32_t firstVertex, const uint32_t vertexCount)
|
|
|
|
|
{
|
|
|
|
|
PreDraw();
|
|
|
|
|
vkCmdDraw(m_CommandContext->GetCommandBuffer(), vertexCount, 1, firstVertex, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::DrawIndexed(
|
|
|
|
|
const uint32_t firstIndex, const uint32_t indexCount, const int32_t vertexOffset)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(vertexOffset == 0);
|
|
|
|
|
PreDraw();
|
|
|
|
|
vkCmdDrawIndexed(m_CommandContext->GetCommandBuffer(), indexCount, 1, firstIndex, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::DrawInstanced(
|
|
|
|
|
const uint32_t firstVertex, const uint32_t vertexCount,
|
|
|
|
|
const uint32_t firstInstance, const uint32_t instanceCount)
|
|
|
|
|
{
|
|
|
|
|
PreDraw();
|
|
|
|
|
vkCmdDraw(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), vertexCount, instanceCount, firstVertex, firstInstance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::DrawIndexedInstanced(
|
|
|
|
|
const uint32_t firstIndex, const uint32_t indexCount,
|
|
|
|
|
const uint32_t firstInstance, const uint32_t instanceCount,
|
|
|
|
|
const int32_t vertexOffset)
|
|
|
|
|
{
|
|
|
|
|
PreDraw();
|
|
|
|
|
vkCmdDrawIndexed(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::DrawIndexedInRange(
|
|
|
|
|
const uint32_t firstIndex, const uint32_t indexCount,
|
|
|
|
|
const uint32_t UNUSED(start), const uint32_t UNUSED(end))
|
|
|
|
|
{
|
|
|
|
|
DrawIndexed(firstIndex, indexCount, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 11:40:27 -08:00
|
|
|
void CDeviceCommandContext::BeginComputePass()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
ENSURE(!m_InsideComputePass);
|
|
|
|
|
m_InsideComputePass = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::EndComputePass()
|
|
|
|
|
{
|
|
|
|
|
if (m_ShaderProgram)
|
|
|
|
|
{
|
|
|
|
|
m_ShaderProgram->Unbind();
|
|
|
|
|
m_ShaderProgram = nullptr;
|
|
|
|
|
}
|
2024-10-07 11:16:51 -07:00
|
|
|
m_LastBoundPipeline = VK_NULL_HANDLE;
|
|
|
|
|
m_ComputePipelineState = nullptr;
|
2024-01-17 11:40:27 -08:00
|
|
|
|
|
|
|
|
ENSURE(m_InsideComputePass);
|
|
|
|
|
m_InsideComputePass = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::Dispatch(
|
|
|
|
|
const uint32_t groupCountX,
|
|
|
|
|
const uint32_t groupCountY,
|
|
|
|
|
const uint32_t groupCountZ)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsideComputePass);
|
2024-10-07 11:16:51 -07:00
|
|
|
ApplyPipelineStateIfDirty();
|
2024-01-17 11:40:27 -08:00
|
|
|
m_ShaderProgram->PreDispatch(*m_CommandContext);
|
|
|
|
|
UpdateOutdatedConstants();
|
|
|
|
|
vkCmdDispatch(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), groupCountX, groupCountY, groupCountZ);
|
|
|
|
|
m_ShaderProgram->PostDispatch(*m_CommandContext);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-09 13:47:15 -08:00
|
|
|
void CDeviceCommandContext::InsertMemoryBarrier(
|
|
|
|
|
const uint32_t srcStageMask, const uint32_t dstStageMask,
|
|
|
|
|
const uint32_t srcAccessMask, const uint32_t dstAccessMask)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
Utilities::SubmitMemoryBarrier(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(),
|
|
|
|
|
Mapping::FromAccessMask(srcAccessMask), Mapping::FromAccessMask(dstAccessMask),
|
|
|
|
|
Mapping::FromPipelineStageMask(srcStageMask), Mapping::FromPipelineStageMask(dstStageMask));
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 12:22:20 -08:00
|
|
|
void CDeviceCommandContext::SetTexture(const int32_t bindingSlot, ITexture* texture)
|
|
|
|
|
{
|
|
|
|
|
if (bindingSlot < 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(m_InsidePass || m_InsideComputePass);
|
2023-01-10 12:22:20 -08:00
|
|
|
ENSURE(texture);
|
|
|
|
|
CTexture* textureToBind = texture->As<CTexture>();
|
|
|
|
|
ENSURE(textureToBind->GetUsage() & ITexture::Usage::SAMPLED);
|
|
|
|
|
|
2024-01-17 11:40:27 -08:00
|
|
|
if (!m_Device->GetDescriptorManager().UseDescriptorIndexing() && m_InsidePass)
|
2023-01-10 12:22:20 -08:00
|
|
|
{
|
2023-06-05 09:32:18 -07:00
|
|
|
// We can't bind textures which are used as attachments.
|
2023-01-10 12:22:20 -08:00
|
|
|
const auto& colorAttachments = m_Framebuffer->GetColorAttachments();
|
|
|
|
|
ENSURE(std::find(
|
|
|
|
|
colorAttachments.begin(), colorAttachments.end(), textureToBind) == colorAttachments.end());
|
|
|
|
|
ENSURE(m_Framebuffer->GetDepthStencilAttachment() != textureToBind);
|
|
|
|
|
|
|
|
|
|
ENSURE(textureToBind->IsInitialized());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_ShaderProgram->SetTexture(bindingSlot, textureToBind);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 11:40:27 -08:00
|
|
|
void CDeviceCommandContext::SetStorageTexture(const int32_t bindingSlot, ITexture* texture)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsidePass || m_InsideComputePass);
|
|
|
|
|
ENSURE(texture);
|
|
|
|
|
CTexture* textureToBind = texture->As<CTexture>();
|
|
|
|
|
ENSURE(textureToBind->GetUsage() & ITexture::Usage::STORAGE);
|
|
|
|
|
m_ShaderProgram->SetStorageTexture(bindingSlot, textureToBind);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-09 13:47:15 -08:00
|
|
|
void CDeviceCommandContext::SetStorageBuffer(const int32_t bindingSlot, IBuffer* buffer)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsidePass || m_InsideComputePass);
|
|
|
|
|
ENSURE(buffer);
|
|
|
|
|
CBuffer* bufferToBind = buffer->As<CBuffer>();
|
|
|
|
|
ENSURE(bufferToBind->GetType() == IBuffer::Type::VERTEX || bufferToBind->GetType() == IBuffer::Type::INDEX);
|
|
|
|
|
ENSURE(bufferToBind->GetUsage() & IBuffer::Usage::STORAGE);
|
|
|
|
|
m_ShaderProgram->SetStorageBuffer(bindingSlot, bufferToBind);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 12:22:20 -08:00
|
|
|
void CDeviceCommandContext::SetUniform(
|
|
|
|
|
const int32_t bindingSlot,
|
|
|
|
|
const float value)
|
|
|
|
|
{
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(m_InsidePass || m_InsideComputePass);
|
2023-01-10 12:22:20 -08:00
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetUniform(
|
|
|
|
|
const int32_t bindingSlot,
|
|
|
|
|
const float valueX, const float valueY)
|
|
|
|
|
{
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(m_InsidePass || m_InsideComputePass);
|
2023-01-10 12:22:20 -08:00
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, valueX, valueY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetUniform(
|
|
|
|
|
const int32_t bindingSlot,
|
|
|
|
|
const float valueX, const float valueY,
|
|
|
|
|
const float valueZ)
|
|
|
|
|
{
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(m_InsidePass || m_InsideComputePass);
|
2023-01-10 12:22:20 -08:00
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, valueX, valueY, valueZ);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetUniform(
|
|
|
|
|
const int32_t bindingSlot,
|
|
|
|
|
const float valueX, const float valueY,
|
|
|
|
|
const float valueZ, const float valueW)
|
|
|
|
|
{
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(m_InsidePass || m_InsideComputePass);
|
2023-01-10 12:22:20 -08:00
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, valueX, valueY, valueZ, valueW);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::SetUniform(
|
|
|
|
|
const int32_t bindingSlot, PS::span<const float> values)
|
|
|
|
|
{
|
2024-01-17 11:40:27 -08:00
|
|
|
ENSURE(m_InsidePass || m_InsideComputePass);
|
2023-01-10 12:22:20 -08:00
|
|
|
m_ShaderProgram->SetUniform(bindingSlot, values);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-16 13:38:32 -07:00
|
|
|
void CDeviceCommandContext::InsertTimestampQuery(const uint32_t handle, const bool isScopeBegin)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
m_Device->InsertTimestampQuery(m_CommandContext->GetCommandBuffer(), handle, isScopeBegin);
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 12:22:20 -08:00
|
|
|
void CDeviceCommandContext::BeginScopedLabel(const char* name)
|
|
|
|
|
{
|
|
|
|
|
if (!m_DebugScopedLabels)
|
|
|
|
|
return;
|
|
|
|
|
VkDebugUtilsLabelEXT label{};
|
|
|
|
|
label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
|
|
|
|
label.pLabelName = name;
|
|
|
|
|
vkCmdBeginDebugUtilsLabelEXT(m_CommandContext->GetCommandBuffer(), &label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::EndScopedLabel()
|
|
|
|
|
{
|
|
|
|
|
if (!m_DebugScopedLabels)
|
|
|
|
|
return;
|
|
|
|
|
vkCmdEndDebugUtilsLabelEXT(m_CommandContext->GetCommandBuffer());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::Flush()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(!m_InsideFramebufferPass);
|
|
|
|
|
// TODO: fix unsafe copying when overlaping flushes/frames.
|
|
|
|
|
|
2023-02-01 14:09:25 -08:00
|
|
|
m_VertexUploadRing->ExecuteUploads(m_PrependCommandContext->GetCommandBuffer());
|
|
|
|
|
m_IndexUploadRing->ExecuteUploads(m_PrependCommandContext->GetCommandBuffer());
|
|
|
|
|
m_UniformUploadRing->ExecuteUploads(m_PrependCommandContext->GetCommandBuffer());
|
2023-01-10 12:22:20 -08:00
|
|
|
|
|
|
|
|
m_IsPipelineStateDirty = true;
|
|
|
|
|
|
2023-02-17 09:36:10 -08:00
|
|
|
CTexture* backbufferReadbackTexture = m_QueuedReadbacks.empty()
|
|
|
|
|
? nullptr : m_Device->GetOrCreateBackbufferReadbackTexture();
|
|
|
|
|
const bool needsReadback = backbufferReadbackTexture;
|
|
|
|
|
if (needsReadback)
|
|
|
|
|
{
|
|
|
|
|
CTexture* backbufferTexture = m_Device->GetCurrentBackbufferTexture();
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// We assume that the readback texture is in linear tiling.
|
|
|
|
|
ScopedImageLayoutTransition scopedBackbufferTransition{
|
|
|
|
|
*m_CommandContext,
|
|
|
|
|
{&backbufferTexture, 1},
|
|
|
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
|
|
|
VK_ACCESS_TRANSFER_READ_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT};
|
|
|
|
|
ScopedImageLayoutTransition scopedReadbackBackbufferTransition{
|
|
|
|
|
*m_CommandContext,
|
|
|
|
|
{&backbufferReadbackTexture, 1},
|
|
|
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
|
|
|
VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT};
|
|
|
|
|
VkImageCopy region{};
|
|
|
|
|
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
region.srcSubresource.layerCount = 1;
|
|
|
|
|
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
region.dstSubresource.layerCount = 1;
|
|
|
|
|
region.extent.width = backbufferTexture->GetWidth();
|
|
|
|
|
region.extent.height = backbufferTexture->GetHeight();
|
|
|
|
|
region.extent.depth = 1;
|
|
|
|
|
vkCmdCopyImage(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(),
|
|
|
|
|
backbufferTexture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
|
|
|
backbufferReadbackTexture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
|
|
|
1, ®ion);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Utilities::SubmitMemoryBarrier(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(),
|
|
|
|
|
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT);
|
|
|
|
|
|
|
|
|
|
m_PrependCommandContext->Flush();
|
|
|
|
|
m_CommandContext->FlushAndWait();
|
|
|
|
|
|
|
|
|
|
VkImageSubresource subresource{};
|
|
|
|
|
subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
VkSubresourceLayout subresourceLayout{};
|
|
|
|
|
vkGetImageSubresourceLayout(
|
|
|
|
|
m_Device->GetVkDevice(), backbufferReadbackTexture->GetImage(), &subresource, &subresourceLayout);
|
|
|
|
|
|
|
|
|
|
void* mappedData = backbufferReadbackTexture->GetMappedData();
|
|
|
|
|
ENSURE(mappedData);
|
|
|
|
|
const uint32_t height = backbufferReadbackTexture->GetHeight();
|
|
|
|
|
const auto [redOffset, greenOffset, blueOffset] = backbufferReadbackTexture->GetFormat() == Format::B8G8R8A8_UNORM
|
|
|
|
|
? std::make_tuple(2, 1, 0) : std::make_tuple(0, 1, 2);
|
|
|
|
|
for (const QueuedReadback& queuedReackback : m_QueuedReadbacks)
|
|
|
|
|
{
|
|
|
|
|
const std::byte* data = static_cast<const std::byte*>(mappedData);
|
|
|
|
|
// Currently the backbuffer (0, 0) is the left-bottom corner (legacy from GL).
|
|
|
|
|
data += subresourceLayout.offset + subresourceLayout.rowPitch * (height - queuedReackback.height - queuedReackback.y);
|
|
|
|
|
for (uint32_t y = 0; y < queuedReackback.height; ++y)
|
|
|
|
|
{
|
|
|
|
|
const std::byte* row = data;
|
|
|
|
|
for (uint32_t x = 0; x < queuedReackback.width; ++x)
|
|
|
|
|
{
|
|
|
|
|
const uint32_t sourceIndex = (queuedReackback.x + x) * 4;
|
|
|
|
|
const uint32_t destinationIndex = ((queuedReackback.height - y - 1) * queuedReackback.width + x) * 3;
|
|
|
|
|
std::byte* destinationPixelData = static_cast<std::byte*>(queuedReackback.data) + destinationIndex;
|
|
|
|
|
destinationPixelData[0] = row[sourceIndex + redOffset];
|
|
|
|
|
destinationPixelData[1] = row[sourceIndex + greenOffset];
|
|
|
|
|
destinationPixelData[2] = row[sourceIndex + blueOffset];
|
|
|
|
|
}
|
|
|
|
|
data += subresourceLayout.rowPitch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_PrependCommandContext->Flush();
|
|
|
|
|
m_CommandContext->Flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_QueuedReadbacks.clear();
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::PreDraw()
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_InsidePass);
|
|
|
|
|
ApplyPipelineStateIfDirty();
|
2023-11-07 13:43:52 -08:00
|
|
|
m_ShaderProgram->PreDraw(*m_CommandContext);
|
2024-01-17 11:40:27 -08:00
|
|
|
UpdateOutdatedConstants();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::UpdateOutdatedConstants()
|
|
|
|
|
{
|
2023-01-10 12:22:20 -08:00
|
|
|
if (m_ShaderProgram->IsMaterialConstantsDataOutdated())
|
|
|
|
|
{
|
|
|
|
|
const VkDeviceSize alignment =
|
|
|
|
|
std::max(static_cast<VkDeviceSize>(16), m_Device->GetChoosenPhysicalDevice().properties.limits.minUniformBufferOffsetAlignment);
|
2023-02-01 14:09:25 -08:00
|
|
|
const uint32_t offset = m_UniformUploadRing->ScheduleUpload(
|
|
|
|
|
m_PrependCommandContext->GetCommandBuffer(),
|
|
|
|
|
PS::span<const std::byte>{
|
|
|
|
|
m_ShaderProgram->GetMaterialConstantsData(),
|
|
|
|
|
m_ShaderProgram->GetMaterialConstantsDataSize()}, alignment);
|
2023-01-10 12:22:20 -08:00
|
|
|
m_ShaderProgram->UpdateMaterialConstantsData();
|
|
|
|
|
|
2024-10-30 12:26:43 -07:00
|
|
|
const VkDescriptorSet uniformDescriptorSet{GetUniformDescriptorSet(
|
|
|
|
|
m_UniformUploadRing->GetBuffer(), m_ShaderProgram->GetMaterialConstantsDataSize())};
|
2023-01-10 12:22:20 -08:00
|
|
|
// TODO: maybe move inside shader program to reduce the # of bind calls.
|
|
|
|
|
vkCmdBindDescriptorSets(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), m_ShaderProgram->GetPipelineBindPoint(),
|
|
|
|
|
m_ShaderProgram->GetPipelineLayout(), m_Device->GetDescriptorManager().GetUniformSet(),
|
2024-10-30 12:26:43 -07:00
|
|
|
1, &uniformDescriptorSet, 1, &offset);
|
2023-01-10 12:22:20 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::ApplyPipelineStateIfDirty()
|
|
|
|
|
{
|
|
|
|
|
if (!m_IsPipelineStateDirty)
|
|
|
|
|
return;
|
|
|
|
|
m_IsPipelineStateDirty = false;
|
|
|
|
|
|
2024-10-07 11:16:51 -07:00
|
|
|
ENSURE(m_GraphicsPipelineState || m_ComputePipelineState);
|
|
|
|
|
if (m_GraphicsPipelineState)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(m_VertexInputLayout);
|
|
|
|
|
ENSURE(m_Framebuffer);
|
|
|
|
|
}
|
2023-01-10 12:22:20 -08:00
|
|
|
|
2024-10-07 11:16:51 -07:00
|
|
|
VkPipeline pipeline{m_GraphicsPipelineState
|
|
|
|
|
? m_GraphicsPipelineState->GetOrCreatePipeline(m_VertexInputLayout, m_Framebuffer)
|
|
|
|
|
: m_ComputePipelineState->GetPipeline()};
|
2023-01-10 12:22:20 -08:00
|
|
|
ENSURE(pipeline != VK_NULL_HANDLE);
|
|
|
|
|
|
|
|
|
|
if (m_LastBoundPipeline != pipeline)
|
|
|
|
|
{
|
|
|
|
|
m_LastBoundPipeline = pipeline;
|
|
|
|
|
vkCmdBindPipeline(m_CommandContext->GetCommandBuffer(), m_ShaderProgram->GetPipelineBindPoint(), pipeline);
|
|
|
|
|
|
|
|
|
|
m_ShaderProgram->Bind();
|
|
|
|
|
|
|
|
|
|
if (m_Device->GetDescriptorManager().UseDescriptorIndexing())
|
|
|
|
|
{
|
|
|
|
|
vkCmdBindDescriptorSets(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), m_ShaderProgram->GetPipelineBindPoint(),
|
|
|
|
|
m_ShaderProgram->GetPipelineLayout(), 0,
|
|
|
|
|
1, &m_Device->GetDescriptorManager().GetDescriptorIndexingSet(), 0, nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::BindVertexBuffer(
|
|
|
|
|
const uint32_t bindingSlot, CBuffer* buffer, uint32_t offset)
|
|
|
|
|
{
|
|
|
|
|
VkBuffer vertexBuffers[] = { buffer->GetVkBuffer() };
|
|
|
|
|
VkDeviceSize offsets[] = { offset };
|
|
|
|
|
vkCmdBindVertexBuffers(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), bindingSlot, std::size(vertexBuffers), vertexBuffers, offsets);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDeviceCommandContext::BindIndexBuffer(CBuffer* buffer, uint32_t offset)
|
|
|
|
|
{
|
|
|
|
|
if (buffer == m_BoundIndexBuffer && offset == m_BoundIndexBufferOffset)
|
|
|
|
|
return;
|
|
|
|
|
m_BoundIndexBuffer = buffer;
|
|
|
|
|
m_BoundIndexBufferOffset = offset;
|
|
|
|
|
vkCmdBindIndexBuffer(
|
|
|
|
|
m_CommandContext->GetCommandBuffer(), buffer->GetVkBuffer(), offset, VK_INDEX_TYPE_UINT16);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-30 12:26:43 -07:00
|
|
|
VkDescriptorSet CDeviceCommandContext::GetUniformDescriptorSet(
|
|
|
|
|
CBuffer* uniformBuffer, const uint32_t dataSize)
|
|
|
|
|
{
|
|
|
|
|
ENSURE(uniformBuffer);
|
|
|
|
|
|
|
|
|
|
if (m_UniformBufferUID == INVALID_DEVICE_OBJECT_UID || m_UniformBufferUID != uniformBuffer->GetUID())
|
|
|
|
|
{
|
|
|
|
|
if (m_UniformDescriptorPool != VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
m_Device->ScheduleObjectToDestroy(
|
|
|
|
|
VK_OBJECT_TYPE_DESCRIPTOR_POOL, m_UniformDescriptorPool, VK_NULL_HANDLE);
|
|
|
|
|
m_UniformDescriptorPool = VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_UniformBufferUID = uniformBuffer->GetUID();
|
|
|
|
|
m_UniformDescriptorSets.clear();
|
|
|
|
|
|
|
|
|
|
constexpr uint32_t initialSize{16};
|
|
|
|
|
const uint32_t maxUniformBufferRange{m_Device->GetChoosenPhysicalDevice().properties.limits.maxUniformBufferRange};
|
|
|
|
|
const uint32_t maxSize{std::min(uniformBuffer->GetSize(), 65536u)};
|
|
|
|
|
|
|
|
|
|
uint32_t maxSets{0};
|
|
|
|
|
for (uint32_t size{initialSize}; size <= maxUniformBufferRange && size <= maxSize; size *= 2)
|
|
|
|
|
++maxSets;
|
|
|
|
|
|
|
|
|
|
VkDescriptorPoolSize descriptorPoolSize{};
|
|
|
|
|
descriptorPoolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
|
|
|
descriptorPoolSize.descriptorCount = maxSets;
|
|
|
|
|
|
|
|
|
|
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{};
|
|
|
|
|
descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
|
|
|
descriptorPoolCreateInfo.poolSizeCount = 1;
|
|
|
|
|
descriptorPoolCreateInfo.pPoolSizes = &descriptorPoolSize;
|
|
|
|
|
descriptorPoolCreateInfo.maxSets = maxSets;
|
|
|
|
|
ENSURE_VK_SUCCESS(vkCreateDescriptorPool(
|
|
|
|
|
m_Device->GetVkDevice(), &descriptorPoolCreateInfo, nullptr, &m_UniformDescriptorPool));
|
|
|
|
|
|
|
|
|
|
PS::StaticVector<VkDescriptorBufferInfo, 16> descriptorBufferInfos;
|
|
|
|
|
PS::StaticVector<VkWriteDescriptorSet, 16> writeDescriptorSets;
|
|
|
|
|
for (uint32_t size{initialSize}; size <= maxUniformBufferRange && size <= maxSize; size *= 2)
|
|
|
|
|
{
|
|
|
|
|
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{};
|
|
|
|
|
descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
|
|
|
descriptorSetAllocateInfo.descriptorPool = m_UniformDescriptorPool;
|
|
|
|
|
descriptorSetAllocateInfo.descriptorSetCount = 1;
|
|
|
|
|
descriptorSetAllocateInfo.pSetLayouts = &m_Device->GetDescriptorManager().GetUniformDescriptorSetLayout();
|
|
|
|
|
|
|
|
|
|
VkDescriptorSet descriptorSet{VK_NULL_HANDLE};
|
|
|
|
|
ENSURE_VK_SUCCESS(vkAllocateDescriptorSets(
|
|
|
|
|
m_Device->GetVkDevice(), &descriptorSetAllocateInfo, &descriptorSet));
|
|
|
|
|
|
|
|
|
|
m_UniformDescriptorSets.emplace_back(descriptorSet, size);
|
|
|
|
|
descriptorBufferInfos.emplace_back(uniformBuffer->GetVkBuffer(), 0u, size);
|
|
|
|
|
|
|
|
|
|
VkWriteDescriptorSet writeDescriptorSet{};
|
|
|
|
|
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
|
|
|
writeDescriptorSet.dstSet = descriptorSet;
|
|
|
|
|
writeDescriptorSet.dstBinding = 0;
|
|
|
|
|
writeDescriptorSet.dstArrayElement = 0;
|
|
|
|
|
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
|
|
|
writeDescriptorSet.descriptorCount = 1;
|
|
|
|
|
writeDescriptorSet.pBufferInfo = &descriptorBufferInfos.back();
|
|
|
|
|
writeDescriptorSets.emplace_back(writeDescriptorSet);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vkUpdateDescriptorSets(
|
|
|
|
|
m_Device->GetVkDevice(), writeDescriptorSets.size(), writeDescriptorSets.data(), 0, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkDescriptorSet descriptorSet{VK_NULL_HANDLE};
|
|
|
|
|
for (const UniformDescriptorSet& uniformDescriptorSet : m_UniformDescriptorSets)
|
|
|
|
|
if (uniformDescriptorSet.size >= dataSize)
|
|
|
|
|
{
|
|
|
|
|
descriptorSet = uniformDescriptorSet.descriptorSet;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return descriptorSet;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-10 12:22:20 -08:00
|
|
|
} // namespace Vulkan
|
|
|
|
|
|
|
|
|
|
} // namespace Backend
|
|
|
|
|
|
|
|
|
|
} // namespace Renderer
|