mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-21 05:09:34 -06:00
VideoBackends: Add AbstractShader and AbstractPipeline classes
This commit is contained in:
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
@ -23,9 +24,12 @@
|
||||
#include "VideoBackends/Vulkan/RasterFont.h"
|
||||
#include "VideoBackends/Vulkan/Renderer.h"
|
||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
||||
#include "VideoBackends/Vulkan/SwapChain.h"
|
||||
#include "VideoBackends/Vulkan/TextureCache.h"
|
||||
#include "VideoBackends/Vulkan/Util.h"
|
||||
#include "VideoBackends/Vulkan/VKPipeline.h"
|
||||
#include "VideoBackends/Vulkan/VKShader.h"
|
||||
#include "VideoBackends/Vulkan/VKTexture.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
|
||||
@ -171,6 +175,202 @@ std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTe
|
||||
return VKStagingTexture::Create(type, config);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractShader> Renderer::CreateShaderFromSource(ShaderStage stage,
|
||||
const char* source, size_t length)
|
||||
{
|
||||
return VKShader::CreateFromSource(stage, source, length);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractShader> Renderer::CreateShaderFromBinary(ShaderStage stage,
|
||||
const void* data, size_t length)
|
||||
{
|
||||
return VKShader::CreateFromBinary(stage, data, length);
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractPipeline> Renderer::CreatePipeline(const AbstractPipelineConfig& config)
|
||||
{
|
||||
return VKPipeline::Create(config);
|
||||
}
|
||||
|
||||
std::tuple<VkBuffer, u32> Renderer::UpdateUtilityUniformBuffer(const void* uniforms,
|
||||
u32 uniforms_size)
|
||||
{
|
||||
StreamBuffer* ubo_buf = g_object_cache->GetUtilityShaderUniformBuffer();
|
||||
if (!ubo_buf->ReserveMemory(uniforms_size, g_vulkan_context->GetUniformBufferAlignment()))
|
||||
{
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||
if (!ubo_buf->ReserveMemory(uniforms_size, g_vulkan_context->GetUniformBufferAlignment()))
|
||||
{
|
||||
PanicAlert("Failed to reserve uniform buffer space for utility draw.");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
VkBuffer ubo = ubo_buf->GetBuffer();
|
||||
u32 ubo_offset = static_cast<u32>(ubo_buf->GetCurrentOffset());
|
||||
std::memcpy(ubo_buf->GetCurrentHostPointer(), uniforms, uniforms_size);
|
||||
ubo_buf->CommitMemory(uniforms_size);
|
||||
|
||||
return std::tie(ubo, ubo_offset);
|
||||
}
|
||||
|
||||
void Renderer::SetPipeline(const AbstractPipeline* pipeline)
|
||||
{
|
||||
m_graphics_pipeline = static_cast<const VKPipeline*>(pipeline);
|
||||
}
|
||||
|
||||
void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices,
|
||||
u32 vertex_stride, u32 num_vertices)
|
||||
{
|
||||
// Binding the utility pipeline layout breaks the standard layout.
|
||||
StateTracker::GetInstance()->SetPendingRebind();
|
||||
|
||||
// Upload uniforms.
|
||||
VkBuffer uniform_buffer = g_object_cache->GetUtilityShaderUniformBuffer()->GetBuffer();
|
||||
u32 uniform_buffer_offset = 0;
|
||||
if (uniforms_size > 0)
|
||||
std::tie(uniform_buffer, uniform_buffer_offset) =
|
||||
UpdateUtilityUniformBuffer(uniforms, uniforms_size);
|
||||
|
||||
// Upload vertices.
|
||||
VkBuffer vertex_buffer = VK_NULL_HANDLE;
|
||||
VkDeviceSize vertex_buffer_offset = 0;
|
||||
if (vertices)
|
||||
{
|
||||
u32 vertices_size = vertex_stride * num_vertices;
|
||||
StreamBuffer* vbo_buf = g_object_cache->GetUtilityShaderVertexBuffer();
|
||||
if (!vbo_buf->ReserveMemory(vertices_size, vertex_stride))
|
||||
{
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(true);
|
||||
if (!vbo_buf->ReserveMemory(vertices_size, vertex_stride))
|
||||
{
|
||||
PanicAlert("Failed to reserve vertex buffer space for utility draw.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vertex_buffer = vbo_buf->GetBuffer();
|
||||
vertex_buffer_offset = vbo_buf->GetCurrentOffset();
|
||||
std::memcpy(vbo_buf->GetCurrentHostPointer(), vertices, vertices_size);
|
||||
vbo_buf->CommitMemory(vertices_size);
|
||||
}
|
||||
|
||||
// Allocate descriptor sets.
|
||||
std::array<VkDescriptorSet, 2> dsets;
|
||||
dsets[0] = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_SINGLE_UNIFORM_BUFFER));
|
||||
dsets[1] = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS));
|
||||
|
||||
// Flush first if failed.
|
||||
if (dsets[0] == VK_NULL_HANDLE || dsets[1] == VK_NULL_HANDLE)
|
||||
{
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(true);
|
||||
dsets[0] = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_SINGLE_UNIFORM_BUFFER));
|
||||
dsets[1] = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS));
|
||||
|
||||
if (dsets[0] == VK_NULL_HANDLE || dsets[1] == VK_NULL_HANDLE)
|
||||
{
|
||||
PanicAlert("Failed to allocate descriptor sets in utility draw.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Build UBO descriptor set.
|
||||
std::array<VkWriteDescriptorSet, 2> dswrites;
|
||||
VkDescriptorBufferInfo dsbuffer = {uniform_buffer, 0, std::max(uniforms_size, 4u)};
|
||||
dswrites[0] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, dsets[0], 0, 0, 1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, nullptr, &dsbuffer, nullptr};
|
||||
dswrites[1] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
dsets[1],
|
||||
0,
|
||||
0,
|
||||
NUM_PIXEL_SHADER_SAMPLERS,
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
StateTracker::GetInstance()->GetPSSamplerBindings().data(),
|
||||
nullptr,
|
||||
nullptr};
|
||||
|
||||
// Build commands.
|
||||
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_graphics_pipeline->GetPipeline());
|
||||
if (vertex_buffer != VK_NULL_HANDLE)
|
||||
vkCmdBindVertexBuffers(command_buffer, 0, 1, &vertex_buffer, &vertex_buffer_offset);
|
||||
|
||||
// Update and bind descriptors.
|
||||
VkPipelineLayout pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_UTILITY);
|
||||
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), static_cast<u32>(dswrites.size()),
|
||||
dswrites.data(), 0, nullptr);
|
||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0,
|
||||
static_cast<u32>(dsets.size()), dsets.data(), 1, &uniform_buffer_offset);
|
||||
|
||||
// Ensure we're in a render pass before drawing, just in case we had to flush.
|
||||
StateTracker::GetInstance()->BeginRenderPass();
|
||||
vkCmdDraw(command_buffer, num_vertices, 1, 0, 0);
|
||||
}
|
||||
|
||||
void Renderer::DispatchComputeShader(const AbstractShader* shader, const void* uniforms,
|
||||
u32 uniforms_size, u32 groups_x, u32 groups_y, u32 groups_z)
|
||||
{
|
||||
// Binding the utility pipeline layout breaks the standard layout.
|
||||
StateTracker::GetInstance()->SetPendingRebind();
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
|
||||
// Upload uniforms.
|
||||
VkBuffer uniform_buffer = g_object_cache->GetUtilityShaderUniformBuffer()->GetBuffer();
|
||||
u32 uniform_buffer_offset = 0;
|
||||
if (uniforms_size > 0)
|
||||
std::tie(uniform_buffer, uniform_buffer_offset) =
|
||||
UpdateUtilityUniformBuffer(uniforms, uniforms_size);
|
||||
|
||||
// Flush first if failed.
|
||||
VkDescriptorSet dset = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_COMPUTE));
|
||||
if (dset == VK_NULL_HANDLE)
|
||||
{
|
||||
Util::ExecuteCurrentCommandsAndRestoreState(true);
|
||||
dset = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_COMPUTE));
|
||||
if (dset == VK_NULL_HANDLE)
|
||||
{
|
||||
PanicAlert("Failed to allocate descriptor sets in utility dispatch.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::array<VkWriteDescriptorSet, 2> dswrites;
|
||||
VkDescriptorBufferInfo dsbuffer = {uniform_buffer, 0, std::max(uniforms_size, 4u)};
|
||||
dswrites[0] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, dset, 0, 0, 1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, nullptr, &dsbuffer, nullptr};
|
||||
dswrites[1] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
dset,
|
||||
1,
|
||||
0,
|
||||
NUM_PIXEL_SHADER_SAMPLERS,
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
StateTracker::GetInstance()->GetPSSamplerBindings().data(),
|
||||
nullptr,
|
||||
nullptr};
|
||||
|
||||
// TODO: Texel buffers, storage images.
|
||||
|
||||
// Build commands.
|
||||
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
||||
VkPipelineLayout pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_UTILITY);
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
static_cast<const VKShader*>(shader)->GetComputePipeline());
|
||||
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), static_cast<u32>(dswrites.size()),
|
||||
dswrites.data(), 0, nullptr);
|
||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1,
|
||||
&dset, 1, &uniform_buffer_offset);
|
||||
vkCmdDispatch(command_buffer, groups_x, groups_y, groups_z);
|
||||
}
|
||||
|
||||
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
|
||||
{
|
||||
u32 backbuffer_width = m_swap_chain->GetWidth();
|
||||
|
Reference in New Issue
Block a user