359 lines
12 KiB
C++
359 lines
12 KiB
C++
#include "stdafx.h"
|
|
|
|
using namespace verus;
|
|
using namespace verus::CGI;
|
|
|
|
CommandBufferVulkan::CommandBufferVulkan()
|
|
{
|
|
}
|
|
|
|
CommandBufferVulkan::~CommandBufferVulkan()
|
|
{
|
|
Done();
|
|
}
|
|
|
|
void CommandBufferVulkan::Init()
|
|
{
|
|
VERUS_INIT();
|
|
VERUS_QREF_RENDERER_VULKAN;
|
|
|
|
VERUS_FOR(i, BaseRenderer::s_ringBufferSize)
|
|
_commandBuffers[i] = pRendererVulkan->CreateCommandBuffer(pRendererVulkan->GetVkCommandPool(i));
|
|
}
|
|
|
|
void CommandBufferVulkan::Done()
|
|
{
|
|
VERUS_DONE(CommandBufferVulkan);
|
|
}
|
|
|
|
void CommandBufferVulkan::InitOneTimeSubmit()
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
VERUS_QREF_RENDERER_VULKAN;
|
|
_oneTimeSubmit = true;
|
|
auto commandPool = pRendererVulkan->GetVkCommandPool(renderer->GetRingBufferIndex());
|
|
auto commandBuffer = pRendererVulkan->CreateCommandBuffer(commandPool);
|
|
VERUS_FOR(i, BaseRenderer::s_ringBufferSize)
|
|
_commandBuffers[i] = commandBuffer;
|
|
Begin();
|
|
}
|
|
|
|
void CommandBufferVulkan::DoneOneTimeSubmit()
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
VERUS_QREF_RENDERER_VULKAN;
|
|
End();
|
|
VkSubmitInfo submitInfo = {};
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = _commandBuffers;
|
|
vkQueueSubmit(pRendererVulkan->GetVkGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE);
|
|
vkQueueWaitIdle(pRendererVulkan->GetVkGraphicsQueue());
|
|
auto commandPool = pRendererVulkan->GetVkCommandPool(renderer->GetRingBufferIndex());
|
|
vkFreeCommandBuffers(pRendererVulkan->GetVkDevice(), commandPool, 1, _commandBuffers);
|
|
_oneTimeSubmit = false;
|
|
}
|
|
|
|
void CommandBufferVulkan::Begin()
|
|
{
|
|
VkResult res = VK_SUCCESS;
|
|
VkCommandBufferBeginInfo vkcbbi = {};
|
|
vkcbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
if (_oneTimeSubmit)
|
|
vkcbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
if (VK_SUCCESS != (res = vkBeginCommandBuffer(GetVkCommandBuffer(), &vkcbbi)))
|
|
throw VERUS_RUNTIME_ERROR << "vkBeginCommandBuffer(), res=" << res;
|
|
}
|
|
|
|
void CommandBufferVulkan::End()
|
|
{
|
|
VkResult res = VK_SUCCESS;
|
|
if (VK_SUCCESS != (res = vkEndCommandBuffer(GetVkCommandBuffer())))
|
|
throw VERUS_RUNTIME_ERROR << "vkEndCommandBuffer(), res=" << res;
|
|
}
|
|
|
|
void CommandBufferVulkan::BeginRenderPass(RPHandle renderPassHandle, FBHandle framebufferHandle, std::initializer_list<Vector4> ilClearValues, bool setViewportAndScissor)
|
|
{
|
|
VERUS_QREF_RENDERER_VULKAN;
|
|
VERUS_QREF_CONST_SETTINGS;
|
|
RendererVulkan::RcFramebuffer framebuffer = pRendererVulkan->GetFramebuffer(framebufferHandle);
|
|
VkRenderPassBeginInfo vkrpbi = {};
|
|
vkrpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
vkrpbi.renderPass = pRendererVulkan->GetRenderPass(renderPassHandle);
|
|
vkrpbi.framebuffer = framebuffer._framebuffer;
|
|
vkrpbi.renderArea.extent.width = framebuffer._width;
|
|
vkrpbi.renderArea.extent.height = framebuffer._height;
|
|
VkClearValue clearValue[VERUS_MAX_FB_ATTACH] = {};
|
|
int count = 0;
|
|
for (const auto& x : ilClearValues)
|
|
{
|
|
memcpy(clearValue[count].color.float32, x.ToPointer(), sizeof(clearValue[count].color.float32));
|
|
count++;
|
|
}
|
|
vkrpbi.clearValueCount = count;
|
|
vkrpbi.pClearValues = clearValue;
|
|
vkCmdBeginRenderPass(GetVkCommandBuffer(), &vkrpbi, VK_SUBPASS_CONTENTS_INLINE);
|
|
if (setViewportAndScissor)
|
|
{
|
|
const Vector4 rc(0, 0, static_cast<float>(framebuffer._width), static_cast<float>(framebuffer._height));
|
|
SetViewport({ rc }, 0, 1);
|
|
SetScissor({ rc });
|
|
}
|
|
}
|
|
|
|
void CommandBufferVulkan::NextSubpass()
|
|
{
|
|
vkCmdNextSubpass(GetVkCommandBuffer(), VK_SUBPASS_CONTENTS_INLINE);
|
|
}
|
|
|
|
void CommandBufferVulkan::EndRenderPass()
|
|
{
|
|
vkCmdEndRenderPass(GetVkCommandBuffer());
|
|
}
|
|
|
|
void CommandBufferVulkan::BindVertexBuffers(GeometryPtr geo, UINT32 bindingsFilter)
|
|
{
|
|
auto& geoVulkan = static_cast<RGeometryVulkan>(*geo);
|
|
VkBuffer buffers[VERUS_MAX_VB];
|
|
VkDeviceSize offsets[VERUS_MAX_VB];
|
|
const int count = geoVulkan.GetVertexBufferCount();
|
|
int at = 0;
|
|
VERUS_FOR(i, count)
|
|
{
|
|
if ((bindingsFilter >> i) & 0x1)
|
|
{
|
|
buffers[at] = geoVulkan.GetVkVertexBuffer(i);
|
|
offsets[at] = geoVulkan.GetVkVertexBufferOffset(i);
|
|
at++;
|
|
}
|
|
}
|
|
vkCmdBindVertexBuffers(GetVkCommandBuffer(), 0, at, buffers, offsets);
|
|
}
|
|
|
|
void CommandBufferVulkan::BindIndexBuffer(GeometryPtr geo)
|
|
{
|
|
auto& geoVulkan = static_cast<RGeometryVulkan>(*geo);
|
|
VkBuffer buffer = geoVulkan.GetVkIndexBuffer();
|
|
VkDeviceSize offset = 0;
|
|
vkCmdBindIndexBuffer(GetVkCommandBuffer(), buffer, offset, geoVulkan.Has32BitIndices() ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16);
|
|
}
|
|
|
|
void CommandBufferVulkan::BindPipeline(PipelinePtr pipe)
|
|
{
|
|
auto& pipeVulkan = static_cast<RPipelineVulkan>(*pipe);
|
|
vkCmdBindPipeline(GetVkCommandBuffer(),
|
|
pipeVulkan.IsCompute() ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
pipeVulkan.GetVkPipeline());
|
|
}
|
|
|
|
void CommandBufferVulkan::SetViewport(std::initializer_list<Vector4> il, float minDepth, float maxDepth)
|
|
{
|
|
if (il.size() > 0)
|
|
{
|
|
const float w = il.begin()->Width();
|
|
const float h = il.begin()->Height();
|
|
_viewportSize = Vector4(w, h, 1 / w, 1 / h);
|
|
}
|
|
|
|
VkViewport vpVulkan[VERUS_MAX_RT];
|
|
int count = 0;
|
|
for (const auto& rc : il)
|
|
{
|
|
vpVulkan[count].x = rc.getX();
|
|
vpVulkan[count].y = rc.getY() + rc.Height();
|
|
vpVulkan[count].width = rc.Width();
|
|
vpVulkan[count].height = -rc.Height();
|
|
vpVulkan[count].minDepth = minDepth;
|
|
vpVulkan[count].maxDepth = maxDepth;
|
|
count++;
|
|
}
|
|
vkCmdSetViewport(GetVkCommandBuffer(), 0, count, vpVulkan);
|
|
}
|
|
|
|
void CommandBufferVulkan::SetScissor(std::initializer_list<Vector4> il)
|
|
{
|
|
VkRect2D rcVulkan[VERUS_MAX_RT];
|
|
int count = 0;
|
|
for (const auto& rc : il)
|
|
{
|
|
rcVulkan[count].offset.x = static_cast<int32_t>(rc.getX());
|
|
rcVulkan[count].offset.y = static_cast<int32_t>(rc.getY());
|
|
rcVulkan[count].extent.width = static_cast<uint32_t>(rc.Width());
|
|
rcVulkan[count].extent.height = static_cast<uint32_t>(rc.Height());
|
|
count++;
|
|
}
|
|
vkCmdSetScissor(GetVkCommandBuffer(), 0, count, rcVulkan);
|
|
}
|
|
|
|
void CommandBufferVulkan::SetBlendConstants(const float* p)
|
|
{
|
|
vkCmdSetBlendConstants(GetVkCommandBuffer(), p);
|
|
}
|
|
|
|
bool CommandBufferVulkan::BindDescriptors(ShaderPtr shader, int setNumber, CSHandle complexSetHandle)
|
|
{
|
|
if (setNumber < 0)
|
|
return true;
|
|
|
|
auto& shaderVulkan = static_cast<RShaderVulkan>(*shader);
|
|
if (shaderVulkan.TryPushConstants(setNumber, *this))
|
|
return true;
|
|
const int offset = shaderVulkan.UpdateUniformBuffer(setNumber);
|
|
if (offset < 0)
|
|
return false;
|
|
|
|
const VkDescriptorSet descriptorSet = complexSetHandle.IsSet() ?
|
|
shaderVulkan.GetComplexVkDescriptorSet(complexSetHandle) : shaderVulkan.GetVkDescriptorSet(setNumber);
|
|
const uint32_t dynamicOffset = offset;
|
|
const uint32_t dynamicOffsetCount = 1;
|
|
vkCmdBindDescriptorSets(GetVkCommandBuffer(),
|
|
shaderVulkan.IsCompute() ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
shaderVulkan.GetVkPipelineLayout(),
|
|
setNumber, 1, &descriptorSet, dynamicOffsetCount, &dynamicOffset);
|
|
return true;
|
|
}
|
|
|
|
void CommandBufferVulkan::PushConstants(ShaderPtr shader, int offset, int size, const void* p, ShaderStageFlags stageFlags)
|
|
{
|
|
auto& shaderVulkan = static_cast<RShaderVulkan>(*shader);
|
|
const VkShaderStageFlags vkssf = ToNativeStageFlags(stageFlags);
|
|
vkCmdPushConstants(GetVkCommandBuffer(), shaderVulkan.GetVkPipelineLayout(), vkssf, offset << 2, size << 2, p);
|
|
}
|
|
|
|
void CommandBufferVulkan::PipelineImageMemoryBarrier(TexturePtr tex, ImageLayout oldLayout, ImageLayout newLayout, Range<int> mipLevels, int arrayLayer)
|
|
{
|
|
auto& texVulkan = static_cast<RTextureVulkan>(*tex);
|
|
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; // Waiting for this stage to finish (TOP_OF_PIPE means wait for nothing).
|
|
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; // Which stage is waiting to start (BOTTOM_OF_PIPE means nothing is waiting).
|
|
const VkPipelineStageFlags dstStageMaskXS = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
VkImageMemoryBarrier vkimb[16];
|
|
VERUS_RT_ASSERT(mipLevels.GetRange() < VERUS_COUNT_OF(vkimb));
|
|
int index = 0;
|
|
for (int mip : mipLevels)
|
|
{
|
|
vkimb[index].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
vkimb[index].pNext = nullptr;
|
|
vkimb[index].oldLayout = ToNativeImageLayout(oldLayout);
|
|
vkimb[index].newLayout = ToNativeImageLayout(newLayout);
|
|
vkimb[index].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
vkimb[index].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
vkimb[index].image = texVulkan.GetVkImage();
|
|
vkimb[index].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
vkimb[index].subresourceRange.baseMipLevel = mip;
|
|
vkimb[index].subresourceRange.levelCount = 1;
|
|
vkimb[index].subresourceRange.baseArrayLayer = arrayLayer;
|
|
vkimb[index].subresourceRange.layerCount = 1;
|
|
|
|
// See: https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples
|
|
|
|
auto UseDefaultsForOldLayout = [&vkimb, index, &srcStageMask]()
|
|
{
|
|
vkimb[index].srcAccessMask = 0;
|
|
switch (vkimb[index].oldLayout)
|
|
{
|
|
case VK_IMAGE_LAYOUT_UNDEFINED:
|
|
case VK_IMAGE_LAYOUT_GENERAL:
|
|
srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
|
srcStageMask = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
|
|
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
|
srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
|
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
|
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
vkimb[index].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
break;
|
|
default:
|
|
VERUS_RT_FAIL("Unknown oldLayout");
|
|
}
|
|
};
|
|
|
|
auto UseDefaultsForNewLayout = [&vkimb, index, &dstStageMask, dstStageMaskXS, newLayout, tex]()
|
|
{
|
|
switch (vkimb[index].newLayout)
|
|
{
|
|
case VK_IMAGE_LAYOUT_UNDEFINED:
|
|
case VK_IMAGE_LAYOUT_GENERAL:
|
|
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
vkimb[index].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
|
dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
|
vkimb[index].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
|
|
dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
vkimb[index].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
|
dstStageMask = (ImageLayout::xsReadOnly == newLayout) ? dstStageMaskXS : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
vkimb[index].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
|
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
vkimb[index].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
|
break;
|
|
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
|
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
vkimb[index].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
break;
|
|
default:
|
|
VERUS_RT_FAIL("Unknown newLayout");
|
|
}
|
|
|
|
if (vkimb[index].newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ||
|
|
vkimb[index].newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL)
|
|
{
|
|
vkimb[index].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
if (Format::unormD24uintS8 == tex->GetFormat())
|
|
vkimb[index].subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
}
|
|
};
|
|
|
|
if (vkimb[index].oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && vkimb[index].newLayout == VK_IMAGE_LAYOUT_GENERAL)
|
|
{
|
|
UseDefaultsForOldLayout();
|
|
dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
vkimb[index].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
}
|
|
else
|
|
{
|
|
UseDefaultsForOldLayout();
|
|
UseDefaultsForNewLayout();
|
|
}
|
|
|
|
index++;
|
|
}
|
|
vkCmdPipelineBarrier(GetVkCommandBuffer(), srcStageMask, dstStageMask, 0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
index, vkimb);
|
|
}
|
|
|
|
void CommandBufferVulkan::Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
|
|
{
|
|
vkCmdDraw(GetVkCommandBuffer(), vertexCount, instanceCount, firstVertex, firstInstance);
|
|
}
|
|
|
|
void CommandBufferVulkan::DrawIndexed(int indexCount, int instanceCount, int firstIndex, int vertexOffset, int firstInstance)
|
|
{
|
|
vkCmdDrawIndexed(GetVkCommandBuffer(), indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
|
|
}
|
|
|
|
void CommandBufferVulkan::Dispatch(int groupCountX, int groupCountY, int groupCountZ)
|
|
{
|
|
vkCmdDispatch(GetVkCommandBuffer(), groupCountX, groupCountY, groupCountZ);
|
|
}
|
|
|
|
VkCommandBuffer CommandBufferVulkan::GetVkCommandBuffer() const
|
|
{
|
|
VERUS_QREF_RENDERER;
|
|
return _commandBuffers[renderer->GetRingBufferIndex()];
|
|
}
|