renderer_vulkan: Refactor VulkanState class and switch to dynamic rendering
* Dynamic rendering gives us no performance loss on desktop while removing the need to cache and manager many renderpass and framebuffer objects
This commit is contained in:
@ -205,7 +205,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(vk::Format format, u32 w
|
||||
return texture;
|
||||
}
|
||||
|
||||
/*static bool FillSurface(const Surface& surface, const u8* fill_data,
|
||||
static bool FillSurface(const Surface& surface, const u8* fill_data,
|
||||
const Common::Rectangle<u32>& fill_rect) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
@ -223,16 +223,11 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(vk::Format format, u32 w
|
||||
surface->InvalidateAllWatcher();
|
||||
|
||||
if (surface->type == SurfaceType::Color || surface->type == SurfaceType::Texture) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
surface->texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
0);
|
||||
|
||||
Pica::Texture::TextureInfo tex_info{};
|
||||
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(surface->pixel_format);
|
||||
Common::Vec4<u8> color = Pica::Texture::LookupTexture(fill_data, 0, 0, tex_info);
|
||||
|
||||
std::array<GLfloat, 4> color_values = {color.x / 255.f, color.y / 255.f, color.z / 255.f,
|
||||
std::array<float, 4> color_values = {color.x / 255.f, color.y / 255.f, color.z / 255.f,
|
||||
color.w / 255.f};
|
||||
|
||||
state.color_mask.red_enabled = GL_TRUE;
|
||||
@ -278,7 +273,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(vk::Format format, u32 w
|
||||
glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int);
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
}
|
||||
|
||||
CachedSurface::~CachedSurface() {
|
||||
if (texture.IsValid()) {
|
||||
|
@ -10,15 +10,13 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
VKResourceCache::~VKResourceCache()
|
||||
{
|
||||
VKResourceCache::~VKResourceCache() {
|
||||
for (int i = 0; i < DESCRIPTOR_SET_LAYOUT_COUNT; i++) {
|
||||
g_vk_instace->GetDevice().destroyDescriptorSetLayout(descriptor_layouts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool VKResourceCache::Initialize()
|
||||
{
|
||||
bool VKResourceCache::Initialize() {
|
||||
// Define the descriptor sets we will be using
|
||||
std::array<vk::DescriptorSetLayoutBinding, 2> ubo_set = {{
|
||||
{ 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex |
|
||||
@ -54,58 +52,22 @@ bool VKResourceCache::Initialize()
|
||||
vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts);
|
||||
pipeline_layout = g_vk_instace->GetDevice().createPipelineLayoutUnique(layout_info);
|
||||
|
||||
// Create global texture staging buffer
|
||||
texture_upload_buffer.Create(MAX_TEXTURE_UPLOAD_BUFFER_SIZE,
|
||||
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
|
||||
vk::BufferUsageFlagBits::eTransferSrc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
vk::Sampler VKResourceCache::GetSampler(const SamplerInfo& info)
|
||||
{
|
||||
auto iter = sampler_cache.find(info);
|
||||
if (iter != sampler_cache.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
// Create texture sampler
|
||||
auto properties = g_vk_instace->GetPhysicalDevice().getProperties();
|
||||
auto features = g_vk_instace->GetPhysicalDevice().getFeatures();
|
||||
vk::SamplerCreateInfo sampler_info
|
||||
(
|
||||
{},
|
||||
info.mag_filter,
|
||||
info.min_filter,
|
||||
info.mipmap_mode,
|
||||
info.wrapping[0], info.wrapping[1], info.wrapping[2],
|
||||
{},
|
||||
features.samplerAnisotropy,
|
||||
properties.limits.maxSamplerAnisotropy,
|
||||
false,
|
||||
vk::CompareOp::eAlways,
|
||||
{},
|
||||
{},
|
||||
vk::BorderColor::eFloatTransparentBlack,
|
||||
false
|
||||
);
|
||||
|
||||
auto sampler = g_vk_instace->GetDevice().createSamplerUnique(sampler_info);
|
||||
vk::Sampler handle = sampler.get();
|
||||
|
||||
// Store it even if it failed
|
||||
sampler_cache.emplace(info, std::move(sampler));
|
||||
return handle;
|
||||
}
|
||||
|
||||
vk::RenderPass VKResourceCache::GetRenderPass(vk::Format color_format, vk::Format depth_format,
|
||||
u32 multisamples, vk::AttachmentLoadOp load_op)
|
||||
{
|
||||
vk::SampleCountFlagBits multisamples,
|
||||
vk::AttachmentLoadOp load_op) {
|
||||
// Search the cache if we can reuse an already created renderpass
|
||||
auto key = std::tie(color_format, depth_format, multisamples, load_op);
|
||||
auto it = render_pass_cache.find(key);
|
||||
if (it != render_pass_cache.end()) {
|
||||
return it->second;
|
||||
RenderPassCacheKey key = {
|
||||
.color = color_format,
|
||||
.depth = depth_format,
|
||||
.samples = multisamples
|
||||
};
|
||||
|
||||
auto it = renderpass_cache.find(key);
|
||||
if (it != renderpass_cache.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
// Otherwise create a new one with the parameters provided
|
||||
@ -120,7 +82,7 @@ vk::RenderPass VKResourceCache::GetRenderPass(vk::Format color_format, vk::Forma
|
||||
{
|
||||
{},
|
||||
color_format,
|
||||
static_cast<vk::SampleCountFlagBits>(multisamples),
|
||||
multisamples,
|
||||
load_op,
|
||||
vk::AttachmentStoreOp::eStore,
|
||||
vk::AttachmentLoadOp::eDontCare,
|
||||
@ -157,7 +119,7 @@ vk::RenderPass VKResourceCache::GetRenderPass(vk::Format color_format, vk::Forma
|
||||
auto renderpass = g_vk_instace->GetDevice().createRenderPassUnique(renderpass_info);
|
||||
vk::RenderPass handle = renderpass.get();
|
||||
|
||||
render_pass_cache.emplace(key, std::move(renderpass));
|
||||
renderpass_cache.emplace(key, std::move(renderpass));
|
||||
return handle;
|
||||
}
|
||||
} // namespace Vulkan
|
||||
|
@ -15,13 +15,16 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
using RenderPassCacheKey = std::tuple<vk::Format, vk::Format, u32, vk::AttachmentLoadOp>;
|
||||
struct RenderPassCacheKey {
|
||||
vk::Format color, depth;
|
||||
vk::SampleCountFlagBits samples;
|
||||
};
|
||||
|
||||
constexpr u32 MAX_TEXTURE_UPLOAD_BUFFER_SIZE = 32 * 1024 * 1024;
|
||||
constexpr u32 DESCRIPTOR_SET_LAYOUT_COUNT = 3;
|
||||
|
||||
class VKResourceCache
|
||||
{
|
||||
/// Wrapper class that manages resource caching and storage.
|
||||
/// It stores pipelines and renderpasses
|
||||
class VKResourceCache {
|
||||
public:
|
||||
VKResourceCache() = default;
|
||||
~VKResourceCache();
|
||||
@ -31,9 +34,12 @@ public:
|
||||
void Shutdown();
|
||||
|
||||
// Public interface.
|
||||
vk::Sampler GetSampler(const SamplerInfo& info);
|
||||
vk::RenderPass GetRenderPass(vk::Format color_format, vk::Format depth_format, u32 multisamples, vk::AttachmentLoadOp load_op);
|
||||
vk::PipelineCache GetPipelineCache() const { return pipeline_cache.get(); }
|
||||
vk::RenderPass GetRenderPass(vk::Format color_format, vk::Format depth_format,
|
||||
vk::SampleCountFlagBits multisamples,
|
||||
vk::AttachmentLoadOp load_op);
|
||||
|
||||
auto& GetDescriptorLayouts() const { return descriptor_layouts; }
|
||||
|
||||
private:
|
||||
// Descriptor sets
|
||||
@ -41,8 +47,7 @@ private:
|
||||
vk::UniquePipelineLayout pipeline_layout;
|
||||
|
||||
// Render pass cache
|
||||
std::unordered_map<RenderPassCacheKey, vk::RenderPass> render_pass_cache;
|
||||
std::unordered_map<SamplerInfo, vk::Sampler> sampler_cache;
|
||||
std::unordered_map<RenderPassCacheKey, vk::UniqueRenderPass> renderpass_cache;
|
||||
|
||||
vk::UniquePipelineCache pipeline_cache;
|
||||
std::string pipeline_cache_filename;
|
||||
|
@ -3,6 +3,8 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/renderer_vulkan/vk_state.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_cache.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@ -16,6 +18,11 @@ DirtyState operator |=(DirtyState lhs, DirtyState rhs) {
|
||||
);
|
||||
}
|
||||
|
||||
bool operator &(DirtyState lhs, DirtyState rhs) {
|
||||
return static_cast<unsigned>(lhs) &
|
||||
static_cast<unsigned>(rhs);
|
||||
}
|
||||
|
||||
void VulkanState::Create() {
|
||||
// Create a dummy texture which can be used in place of a real binding.
|
||||
VKTexture::Info info = {
|
||||
@ -27,7 +34,24 @@ void VulkanState::Create() {
|
||||
};
|
||||
|
||||
dummy_texture.Create(info);
|
||||
//dummy_texture.TransitionLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
dummy_texture.TransitionLayout(vk::ImageLayout::eShaderReadOnlyOptimal, g_vk_task_scheduler->GetCommandBuffer());
|
||||
|
||||
// Create descriptor pool
|
||||
// TODO: Choose sizes more wisely
|
||||
const std::array<vk::DescriptorPoolSize, 3> pool_sizes{{
|
||||
{ vk::DescriptorType::eUniformBuffer, 32 },
|
||||
{ vk::DescriptorType::eCombinedImageSampler, 32 },
|
||||
{ vk::DescriptorType::eStorageTexelBuffer, 32 },
|
||||
}};
|
||||
|
||||
auto& device = g_vk_instace->GetDevice();
|
||||
vk::DescriptorPoolCreateInfo pool_create_info({}, 1024, pool_sizes);
|
||||
desc_pool = device.createDescriptorPoolUnique(pool_create_info);
|
||||
|
||||
// Create descriptor sets
|
||||
auto& layouts = g_vk_res_cache->GetDescriptorLayouts();
|
||||
vk::DescriptorSetAllocateInfo alloc_info(desc_pool.get(), layouts);
|
||||
descriptor_sets = device.allocateDescriptorSetsUnique(alloc_info);
|
||||
|
||||
dirty_flags |= DirtyState::All;
|
||||
}
|
||||
@ -38,31 +62,19 @@ void VulkanState::SetVertexBuffer(VKBuffer* buffer, vk::DeviceSize offset) {
|
||||
}
|
||||
|
||||
vertex_buffer = buffer;
|
||||
vertex_buffer_offset = offset;
|
||||
vertex_offset = offset;
|
||||
dirty_flags |= DirtyState::VertexBuffer;
|
||||
}
|
||||
|
||||
void VulkanState::SetFramebuffer(VKFramebuffer* buffer) {
|
||||
// Should not be changed within a render pass.
|
||||
//ASSERT(!InRenderPass());
|
||||
//framebuffer = buffer;
|
||||
}
|
||||
|
||||
void VulkanState::SetPipeline(const VKPipeline* new_pipeline) {
|
||||
if (new_pipeline == pipeline)
|
||||
return;
|
||||
|
||||
pipeline = new_pipeline;
|
||||
dirty_flags |= DirtyState::Pipeline;
|
||||
}
|
||||
|
||||
void VulkanState::SetUniformBuffer(UniformID id, VKBuffer* buffer, u32 offset, u32 size) {
|
||||
auto& binding = bindings.ubo[static_cast<u32>(id)];
|
||||
u32 index = static_cast<u32>(id);
|
||||
auto& binding = bindings.ubo[index];
|
||||
if (binding.buffer != buffer->GetBuffer() || binding.range != size)
|
||||
{
|
||||
binding.buffer = buffer->GetBuffer();
|
||||
binding.range = size;
|
||||
dirty_flags |= DirtyState::Uniform;
|
||||
bindings.ubo_update[index] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +87,7 @@ void VulkanState::SetTexture(TextureID id, VKTexture* texture) {
|
||||
bindings.texture[index].imageView = texture->GetView();
|
||||
bindings.texture[index].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||
dirty_flags |= DirtyState::Texture;
|
||||
bindings.texture_update[index] = true;
|
||||
}
|
||||
|
||||
void VulkanState::SetTexelBuffer(TexelBufferID id, VKBuffer* buffer) {
|
||||
@ -85,10 +98,7 @@ void VulkanState::SetTexelBuffer(TexelBufferID id, VKBuffer* buffer) {
|
||||
|
||||
bindings.lut[index].buffer = buffer->GetBuffer();
|
||||
dirty_flags |= DirtyState::TexelBuffer;
|
||||
}
|
||||
|
||||
void VulkanState::SetImageTexture(VKTexture* image) {
|
||||
// TODO
|
||||
bindings.lut_update[index] = true;
|
||||
}
|
||||
|
||||
void VulkanState::UnbindTexture(VKTexture* image) {
|
||||
@ -102,468 +112,140 @@ void VulkanState::UnbindTexture(VKTexture* image) {
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanState::BeginRenderPass()
|
||||
{
|
||||
if (InRenderPass())
|
||||
return;
|
||||
|
||||
m_current_render_pass = m_framebuffer->GetLoadRenderPass();
|
||||
m_framebuffer_render_area = m_framebuffer->GetRect();
|
||||
|
||||
VkRenderPassBeginInfo begin_info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
nullptr,
|
||||
m_current_render_pass,
|
||||
m_framebuffer->GetFB(),
|
||||
m_framebuffer_render_area,
|
||||
0,
|
||||
nullptr};
|
||||
|
||||
vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &begin_info,
|
||||
VK_SUBPASS_CONTENTS_INLINE);
|
||||
void VulkanState::SetAttachments(VKTexture* color, VKTexture* depth_stencil) {
|
||||
color_attachment = color;
|
||||
depth_attachment = depth_stencil;
|
||||
}
|
||||
|
||||
void StateTracker::BeginDiscardRenderPass()
|
||||
{
|
||||
if (InRenderPass())
|
||||
return;
|
||||
|
||||
m_current_render_pass = m_framebuffer->GetDiscardRenderPass();
|
||||
m_framebuffer_render_area = m_framebuffer->GetRect();
|
||||
|
||||
VkRenderPassBeginInfo begin_info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
nullptr,
|
||||
m_current_render_pass,
|
||||
m_framebuffer->GetFB(),
|
||||
m_framebuffer_render_area,
|
||||
0,
|
||||
nullptr};
|
||||
|
||||
vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &begin_info,
|
||||
VK_SUBPASS_CONTENTS_INLINE);
|
||||
void VulkanState::SetRenderArea(vk::Rect2D new_render_area) {
|
||||
render_area = new_render_area;
|
||||
}
|
||||
|
||||
void StateTracker::EndRenderPass()
|
||||
{
|
||||
if (!InRenderPass())
|
||||
return;
|
||||
|
||||
vkCmdEndRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer());
|
||||
m_current_render_pass = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void StateTracker::BeginClearRenderPass(const VkRect2D& area, const VkClearValue* clear_values,
|
||||
u32 num_clear_values)
|
||||
{
|
||||
ASSERT(!InRenderPass());
|
||||
|
||||
m_current_render_pass = m_framebuffer->GetClearRenderPass();
|
||||
m_framebuffer_render_area = area;
|
||||
|
||||
VkRenderPassBeginInfo begin_info = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
nullptr,
|
||||
m_current_render_pass,
|
||||
m_framebuffer->GetFB(),
|
||||
m_framebuffer_render_area,
|
||||
num_clear_values,
|
||||
clear_values};
|
||||
|
||||
vkCmdBeginRenderPass(g_command_buffer_mgr->GetCurrentCommandBuffer(), &begin_info,
|
||||
VK_SUBPASS_CONTENTS_INLINE);
|
||||
}
|
||||
|
||||
void StateTracker::SetViewport(const VkViewport& viewport)
|
||||
{
|
||||
if (memcmp(&m_viewport, &viewport, sizeof(viewport)) == 0)
|
||||
return;
|
||||
|
||||
m_viewport = viewport;
|
||||
m_dirty_flags |= DIRTY_FLAG_VIEWPORT;
|
||||
}
|
||||
|
||||
void StateTracker::SetScissor(const VkRect2D& scissor)
|
||||
{
|
||||
if (memcmp(&m_scissor, &scissor, sizeof(scissor)) == 0)
|
||||
return;
|
||||
|
||||
m_scissor = scissor;
|
||||
m_dirty_flags |= DIRTY_FLAG_SCISSOR;
|
||||
}
|
||||
|
||||
bool StateTracker::Bind()
|
||||
{
|
||||
// Must have a pipeline.
|
||||
if (!m_pipeline)
|
||||
return false;
|
||||
|
||||
// Check the render area if we were in a clear pass.
|
||||
if (m_current_render_pass == m_framebuffer->GetClearRenderPass() && !IsViewportWithinRenderArea())
|
||||
EndRenderPass();
|
||||
|
||||
// Get a new descriptor set if any parts have changed
|
||||
if (!UpdateDescriptorSet())
|
||||
{
|
||||
// We can fail to allocate descriptors if we exhaust the pool for this command buffer.
|
||||
WARN_LOG_FMT(VIDEO, "Failed to get a descriptor set, executing buffer");
|
||||
Renderer::GetInstance()->ExecuteCommandBuffer(false, false);
|
||||
if (!UpdateDescriptorSet())
|
||||
{
|
||||
// Something strange going on.
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to get descriptor set, skipping draw");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Start render pass if not already started
|
||||
if (!InRenderPass())
|
||||
BeginRenderPass();
|
||||
|
||||
// Re-bind parts of the pipeline
|
||||
const VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
||||
if (m_dirty_flags & DIRTY_FLAG_VERTEX_BUFFER)
|
||||
vkCmdBindVertexBuffers(command_buffer, 0, 1, &m_vertex_buffer, &m_vertex_buffer_offset);
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_INDEX_BUFFER)
|
||||
vkCmdBindIndexBuffer(command_buffer, m_index_buffer, m_index_buffer_offset, m_index_type);
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_PIPELINE)
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipeline());
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_VIEWPORT)
|
||||
vkCmdSetViewport(command_buffer, 0, 1, &m_viewport);
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_SCISSOR)
|
||||
vkCmdSetScissor(command_buffer, 0, 1, &m_scissor);
|
||||
|
||||
m_dirty_flags &= ~(DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE |
|
||||
DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StateTracker::BindCompute()
|
||||
{
|
||||
if (!m_compute_shader)
|
||||
return false;
|
||||
|
||||
// Can't kick compute in a render pass.
|
||||
if (InRenderPass())
|
||||
EndRenderPass();
|
||||
|
||||
const VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
||||
if (m_dirty_flags & DIRTY_FLAG_COMPUTE_SHADER)
|
||||
{
|
||||
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
m_compute_shader->GetComputePipeline());
|
||||
}
|
||||
|
||||
if (!UpdateComputeDescriptorSet())
|
||||
{
|
||||
WARN_LOG_FMT(VIDEO, "Failed to get a compute descriptor set, executing buffer");
|
||||
Renderer::GetInstance()->ExecuteCommandBuffer(false, false);
|
||||
if (!UpdateComputeDescriptorSet())
|
||||
{
|
||||
// Something strange going on.
|
||||
ERROR_LOG_FMT(VIDEO, "Failed to get descriptor set, skipping dispatch");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_dirty_flags &= ~DIRTY_FLAG_COMPUTE_SHADER;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StateTracker::IsWithinRenderArea(s32 x, s32 y, u32 width, u32 height) const
|
||||
{
|
||||
// Check that the viewport does not lie outside the render area.
|
||||
// If it does, we need to switch to a normal load/store render pass.
|
||||
s32 left = m_framebuffer_render_area.offset.x;
|
||||
s32 top = m_framebuffer_render_area.offset.y;
|
||||
s32 right = left + static_cast<s32>(m_framebuffer_render_area.extent.width);
|
||||
s32 bottom = top + static_cast<s32>(m_framebuffer_render_area.extent.height);
|
||||
s32 test_left = x;
|
||||
s32 test_top = y;
|
||||
s32 test_right = test_left + static_cast<s32>(width);
|
||||
s32 test_bottom = test_top + static_cast<s32>(height);
|
||||
return test_left >= left && test_right <= right && test_top >= top && test_bottom <= bottom;
|
||||
}
|
||||
|
||||
bool StateTracker::IsViewportWithinRenderArea() const
|
||||
{
|
||||
return IsWithinRenderArea(static_cast<s32>(m_viewport.x), static_cast<s32>(m_viewport.y),
|
||||
static_cast<u32>(m_viewport.width),
|
||||
static_cast<u32>(m_viewport.height));
|
||||
}
|
||||
|
||||
void StateTracker::EndClearRenderPass()
|
||||
{
|
||||
if (m_current_render_pass != m_framebuffer->GetClearRenderPass())
|
||||
return;
|
||||
|
||||
// End clear render pass. Bind() will call BeginRenderPass() which
|
||||
// will switch to the load/store render pass.
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
bool StateTracker::UpdateDescriptorSet()
|
||||
{
|
||||
if (m_pipeline->GetUsage() == AbstractPipelineUsage::GX)
|
||||
return UpdateGXDescriptorSet();
|
||||
else
|
||||
return UpdateUtilityDescriptorSet();
|
||||
}
|
||||
|
||||
bool StateTracker::UpdateGXDescriptorSet()
|
||||
{
|
||||
const size_t MAX_DESCRIPTOR_WRITES = NUM_UBO_DESCRIPTOR_SET_BINDINGS + // UBO
|
||||
1 + // Samplers
|
||||
1; // SSBO
|
||||
std::array<VkWriteDescriptorSet, MAX_DESCRIPTOR_WRITES> writes;
|
||||
u32 num_writes = 0;
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_GX_UBOS || m_gx_descriptor_sets[0] == VK_NULL_HANDLE)
|
||||
{
|
||||
m_gx_descriptor_sets[0] = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_STANDARD_UNIFORM_BUFFERS));
|
||||
if (m_gx_descriptor_sets[0] == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < NUM_UBO_DESCRIPTOR_SET_BINDINGS; i++)
|
||||
{
|
||||
if (i == UBO_DESCRIPTOR_SET_BINDING_GS &&
|
||||
!g_ActiveConfig.backend_info.bSupportsGeometryShaders)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
m_gx_descriptor_sets[0],
|
||||
static_cast<uint32_t>(i),
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
nullptr,
|
||||
&m_bindings.gx_ubo_bindings[i],
|
||||
nullptr};
|
||||
void VulkanState::BeginRendering() {
|
||||
if (rendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_GX_UBOS) | DIRTY_FLAG_DESCRIPTOR_SETS;
|
||||
}
|
||||
// Make sure attachments are in optimal layout
|
||||
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||
if (color_attachment->GetLayout() != vk::ImageLayout::eColorAttachmentOptimal) {
|
||||
color_attachment->TransitionLayout(vk::ImageLayout::eColorAttachmentOptimal, command_buffer);
|
||||
}
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_GX_SAMPLERS || m_gx_descriptor_sets[1] == VK_NULL_HANDLE)
|
||||
{
|
||||
m_gx_descriptor_sets[1] = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_STANDARD_SAMPLERS));
|
||||
if (m_gx_descriptor_sets[1] == VK_NULL_HANDLE)
|
||||
return false;
|
||||
if (depth_attachment->GetLayout() != vk::ImageLayout::eDepthStencilAttachmentOptimal) {
|
||||
depth_attachment->TransitionLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal, command_buffer);
|
||||
}
|
||||
|
||||
writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
m_gx_descriptor_sets[1],
|
||||
0,
|
||||
0,
|
||||
static_cast<u32>(NUM_PIXEL_SHADER_SAMPLERS),
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
m_bindings.samplers.data(),
|
||||
nullptr,
|
||||
nullptr};
|
||||
m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_GX_SAMPLERS) | DIRTY_FLAG_DESCRIPTOR_SETS;
|
||||
}
|
||||
// Begin rendering
|
||||
vk::RenderingAttachmentInfoKHR color_info(color_attachment->GetView(), color_attachment->GetLayout());
|
||||
vk::RenderingAttachmentInfoKHR depth_stencil_info(depth_attachment->GetView(), depth_attachment->GetLayout());
|
||||
|
||||
if (g_ActiveConfig.backend_info.bSupportsBBox &&
|
||||
(m_dirty_flags & DIRTY_FLAG_GX_SSBO || m_gx_descriptor_sets[2] == VK_NULL_HANDLE))
|
||||
{
|
||||
m_gx_descriptor_sets[2] =
|
||||
g_command_buffer_mgr->AllocateDescriptorSet(g_object_cache->GetDescriptorSetLayout(
|
||||
DESCRIPTOR_SET_LAYOUT_STANDARD_SHADER_STORAGE_BUFFERS));
|
||||
if (m_gx_descriptor_sets[2] == VK_NULL_HANDLE)
|
||||
return false;
|
||||
vk::RenderingInfo render_info
|
||||
(
|
||||
{}, render_area, 1, {},
|
||||
color_info,
|
||||
&depth_stencil_info,
|
||||
&depth_stencil_info
|
||||
);
|
||||
|
||||
writes[num_writes++] = {
|
||||
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, m_gx_descriptor_sets[2], 0, 0, 1,
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, nullptr, &m_bindings.ssbo, nullptr};
|
||||
m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_GX_SSBO) | DIRTY_FLAG_DESCRIPTOR_SETS;
|
||||
}
|
||||
|
||||
if (num_writes > 0)
|
||||
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_writes, writes.data(), 0, nullptr);
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_DESCRIPTOR_SETS)
|
||||
{
|
||||
vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(), 0,
|
||||
g_ActiveConfig.backend_info.bSupportsBBox ?
|
||||
NUM_GX_DESCRIPTOR_SETS :
|
||||
(NUM_GX_DESCRIPTOR_SETS - 1),
|
||||
m_gx_descriptor_sets.data(),
|
||||
g_ActiveConfig.backend_info.bSupportsGeometryShaders ?
|
||||
NUM_UBO_DESCRIPTOR_SET_BINDINGS :
|
||||
(NUM_UBO_DESCRIPTOR_SET_BINDINGS - 1),
|
||||
m_bindings.gx_ubo_offsets.data());
|
||||
m_dirty_flags &= ~(DIRTY_FLAG_DESCRIPTOR_SETS | DIRTY_FLAG_GX_UBO_OFFSETS);
|
||||
}
|
||||
else if (m_dirty_flags & DIRTY_FLAG_GX_UBO_OFFSETS)
|
||||
{
|
||||
vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(), 0,
|
||||
1, m_gx_descriptor_sets.data(),
|
||||
g_ActiveConfig.backend_info.bSupportsGeometryShaders ?
|
||||
NUM_UBO_DESCRIPTOR_SET_BINDINGS :
|
||||
(NUM_UBO_DESCRIPTOR_SET_BINDINGS - 1),
|
||||
m_bindings.gx_ubo_offsets.data());
|
||||
m_dirty_flags &= ~DIRTY_FLAG_GX_UBO_OFFSETS;
|
||||
}
|
||||
|
||||
return true;
|
||||
command_buffer.beginRendering(render_info);
|
||||
rendering = true;
|
||||
}
|
||||
|
||||
bool StateTracker::UpdateUtilityDescriptorSet()
|
||||
{
|
||||
// Max number of updates - UBO, Samplers, TexelBuffer
|
||||
std::array<VkWriteDescriptorSet, 3> dswrites;
|
||||
u32 writes = 0;
|
||||
void VulkanState::EndRendering() {
|
||||
if (!rendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate descriptor sets.
|
||||
if (m_dirty_flags & DIRTY_FLAG_UTILITY_UBO || m_utility_descriptor_sets[0] == VK_NULL_HANDLE)
|
||||
{
|
||||
m_utility_descriptor_sets[0] = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UTILITY_UNIFORM_BUFFER));
|
||||
if (!m_utility_descriptor_sets[0])
|
||||
return false;
|
||||
|
||||
dswrites[writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
m_utility_descriptor_sets[0],
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
nullptr,
|
||||
&m_bindings.utility_ubo_binding,
|
||||
nullptr};
|
||||
|
||||
m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_UTILITY_UBO) | DIRTY_FLAG_DESCRIPTOR_SETS;
|
||||
}
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_UTILITY_BINDINGS || m_utility_descriptor_sets[1] == VK_NULL_HANDLE)
|
||||
{
|
||||
m_utility_descriptor_sets[1] = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UTILITY_SAMPLERS));
|
||||
if (!m_utility_descriptor_sets[1])
|
||||
return false;
|
||||
|
||||
dswrites[writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
m_utility_descriptor_sets[1],
|
||||
0,
|
||||
0,
|
||||
NUM_PIXEL_SHADER_SAMPLERS,
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
m_bindings.samplers.data(),
|
||||
nullptr,
|
||||
nullptr};
|
||||
dswrites[writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
m_utility_descriptor_sets[1],
|
||||
8,
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
|
||||
nullptr,
|
||||
nullptr,
|
||||
m_bindings.texel_buffers.data()};
|
||||
|
||||
m_dirty_flags = (m_dirty_flags & ~DIRTY_FLAG_UTILITY_BINDINGS) | DIRTY_FLAG_DESCRIPTOR_SETS;
|
||||
}
|
||||
|
||||
if (writes > 0)
|
||||
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), writes, dswrites.data(), 0, nullptr);
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_DESCRIPTOR_SETS)
|
||||
{
|
||||
vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(), 0,
|
||||
NUM_UTILITY_DESCRIPTOR_SETS, m_utility_descriptor_sets.data(), 1,
|
||||
&m_bindings.utility_ubo_offset);
|
||||
m_dirty_flags &= ~(DIRTY_FLAG_DESCRIPTOR_SETS | DIRTY_FLAG_UTILITY_UBO_OFFSET);
|
||||
}
|
||||
else if (m_dirty_flags & DIRTY_FLAG_UTILITY_UBO_OFFSET)
|
||||
{
|
||||
vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(), 0,
|
||||
1, m_utility_descriptor_sets.data(), 1, &m_bindings.utility_ubo_offset);
|
||||
m_dirty_flags &= ~(DIRTY_FLAG_DESCRIPTOR_SETS | DIRTY_FLAG_UTILITY_UBO_OFFSET);
|
||||
}
|
||||
|
||||
return true;
|
||||
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||
command_buffer.endRendering();
|
||||
rendering = false;
|
||||
}
|
||||
|
||||
bool StateTracker::UpdateComputeDescriptorSet()
|
||||
{
|
||||
// Max number of updates - UBO, Samplers, TexelBuffer, Image
|
||||
std::array<VkWriteDescriptorSet, 4> dswrites;
|
||||
void VulkanState::SetViewport(vk::Viewport new_viewport) {
|
||||
if (new_viewport == viewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate descriptor sets.
|
||||
if (m_dirty_flags & DIRTY_FLAG_COMPUTE_BINDINGS)
|
||||
{
|
||||
m_compute_descriptor_set = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_COMPUTE));
|
||||
dswrites[0] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
m_compute_descriptor_set,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
nullptr,
|
||||
&m_bindings.utility_ubo_binding,
|
||||
nullptr};
|
||||
dswrites[1] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
m_compute_descriptor_set,
|
||||
1,
|
||||
0,
|
||||
NUM_COMPUTE_SHADER_SAMPLERS,
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
m_bindings.samplers.data(),
|
||||
nullptr,
|
||||
nullptr};
|
||||
dswrites[2] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
m_compute_descriptor_set,
|
||||
3,
|
||||
0,
|
||||
NUM_COMPUTE_TEXEL_BUFFERS,
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
|
||||
nullptr,
|
||||
nullptr,
|
||||
m_bindings.texel_buffers.data()};
|
||||
dswrites[3] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
nullptr,
|
||||
m_compute_descriptor_set,
|
||||
5,
|
||||
0,
|
||||
1,
|
||||
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
&m_bindings.image_texture,
|
||||
nullptr,
|
||||
nullptr};
|
||||
viewport = new_viewport;
|
||||
dirty_flags |= DirtyState::Viewport;
|
||||
}
|
||||
|
||||
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), static_cast<uint32_t>(dswrites.size()),
|
||||
dswrites.data(), 0, nullptr);
|
||||
m_dirty_flags =
|
||||
(m_dirty_flags & ~DIRTY_FLAG_COMPUTE_BINDINGS) | DIRTY_FLAG_COMPUTE_DESCRIPTOR_SET;
|
||||
}
|
||||
void VulkanState::SetScissor(vk::Rect2D new_scissor) {
|
||||
if (new_scissor == scissor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_dirty_flags & DIRTY_FLAG_COMPUTE_DESCRIPTOR_SET)
|
||||
{
|
||||
vkCmdBindDescriptorSets(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_COMPUTE), 0, 1,
|
||||
&m_compute_descriptor_set, 1, &m_bindings.utility_ubo_offset);
|
||||
m_dirty_flags &= ~DIRTY_FLAG_COMPUTE_DESCRIPTOR_SET;
|
||||
}
|
||||
scissor = new_scissor;
|
||||
dirty_flags |= DirtyState::Scissor;
|
||||
}
|
||||
|
||||
return true;
|
||||
void VulkanState::Apply() {
|
||||
// Update resources in descriptor sets if changed
|
||||
UpdateDescriptorSet();
|
||||
|
||||
// Start rendering if not already started
|
||||
BeginRendering();
|
||||
|
||||
// Re-apply dynamic parts of the pipeline
|
||||
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||
if (dirty_flags & DirtyState::VertexBuffer) {
|
||||
command_buffer.bindVertexBuffers(0, vertex_buffer->GetBuffer(), vertex_offset);
|
||||
}
|
||||
|
||||
if (dirty_flags & DirtyState::IndexBuffer) {
|
||||
command_buffer.bindIndexBuffer(index_buffer->GetBuffer(), index_offset, vk::IndexType::eUint16);
|
||||
}
|
||||
|
||||
if (dirty_flags & DirtyState::Viewport) {
|
||||
command_buffer.setViewport(0, viewport);
|
||||
}
|
||||
|
||||
if (dirty_flags & DirtyState::Scissor) {
|
||||
command_buffer.setScissor(0, scissor);
|
||||
}
|
||||
|
||||
dirty_flags = DirtyState::None;
|
||||
}
|
||||
|
||||
void VulkanState::UpdateDescriptorSet() {
|
||||
std::vector<vk::WriteDescriptorSet> writes;
|
||||
auto& device = g_vk_instace->GetDevice();
|
||||
|
||||
// Check if any resource has been updated
|
||||
if (dirty_flags & DirtyState::Uniform) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (bindings.ubo_update[i]) {
|
||||
writes.emplace_back(descriptor_sets[i].get(), i, 0, 1, vk::DescriptorType::eUniformBuffer,
|
||||
nullptr, &bindings.ubo[i]);
|
||||
bindings.ubo_update[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty_flags & DirtyState::Texture) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (bindings.texture_update[i]) {
|
||||
writes.emplace_back(descriptor_sets[i].get(), i, 0, 1, vk::DescriptorType::eCombinedImageSampler,
|
||||
nullptr, &bindings.texture[i]);
|
||||
bindings.texture_update[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty_flags & DirtyState::TexelBuffer) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (bindings.lut_update[i]) {
|
||||
writes.emplace_back(descriptor_sets[i].get(), i, 0, 1, vk::DescriptorType::eStorageTexelBuffer,
|
||||
nullptr, &bindings.lut[i]);
|
||||
bindings.lut_update[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!writes.empty()) {
|
||||
device.updateDescriptorSets(writes, {});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -6,12 +6,11 @@
|
||||
|
||||
#include <array>
|
||||
#include "video_core/renderer_vulkan/vk_texture.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
enum class DirtyState {
|
||||
All,
|
||||
None,
|
||||
Framebuffer,
|
||||
Pipeline,
|
||||
Texture,
|
||||
@ -25,7 +24,9 @@ enum class DirtyState {
|
||||
Scissor,
|
||||
CullMode,
|
||||
VertexBuffer,
|
||||
Uniform
|
||||
IndexBuffer,
|
||||
Uniform,
|
||||
All
|
||||
};
|
||||
|
||||
enum class UniformID {
|
||||
@ -57,29 +58,33 @@ public:
|
||||
|
||||
/// Configure drawing state
|
||||
void SetVertexBuffer(VKBuffer* buffer, vk::DeviceSize offset);
|
||||
void SetFramebuffer(VKFramebuffer* framebuffer);
|
||||
void SetPipeline(const VKPipeline* pipeline);
|
||||
void SetViewport(vk::Viewport viewport);
|
||||
void SetScissor(vk::Rect2D scissor);
|
||||
|
||||
/// Rendering
|
||||
void SetAttachments(VKTexture* color, VKTexture* depth_stencil);
|
||||
void SetRenderArea(vk::Rect2D render_area);
|
||||
void BeginRendering();
|
||||
void EndRendering();
|
||||
|
||||
/// Configure shader resources
|
||||
void SetUniformBuffer(UniformID id, VKBuffer* buffer, u32 offset, u32 size);
|
||||
void SetTexture(TextureID id, VKTexture* texture);
|
||||
void SetTexelBuffer(TexelBufferID id, VKBuffer* buffer);
|
||||
void SetImageTexture(VKTexture* image);
|
||||
void UnbindTexture(VKTexture* image);
|
||||
|
||||
/// Apply all dirty state to the current Vulkan command buffer
|
||||
void UpdateDescriptorSet();
|
||||
void Apply();
|
||||
|
||||
private:
|
||||
// Stage which should be applied
|
||||
DirtyState dirty_flags;
|
||||
bool rendering = false;
|
||||
|
||||
// Input assembly
|
||||
VKBuffer* vertex_buffer = nullptr;
|
||||
vk::DeviceSize vertex_buffer_offset = 0;
|
||||
|
||||
// Pipeline state
|
||||
const VKPipeline* pipeline = nullptr;
|
||||
VKBuffer* vertex_buffer = nullptr, * index_buffer = nullptr;
|
||||
vk::DeviceSize vertex_offset = 0, index_offset = 0;
|
||||
|
||||
// Shader bindings. These describe which resources
|
||||
// we have bound to the pipeline and at which
|
||||
@ -89,22 +94,24 @@ private:
|
||||
struct
|
||||
{
|
||||
std::array<vk::DescriptorBufferInfo, 2> ubo;
|
||||
std::array<bool, 2> ubo_update;
|
||||
std::array<vk::DescriptorImageInfo, 4> texture;
|
||||
std::array<bool, 4> texture_update;
|
||||
std::array<vk::DescriptorBufferInfo, 3> lut;
|
||||
std::array<bool, 3> lut_update;
|
||||
} bindings = {};
|
||||
|
||||
std::array<vk::DescriptorSet, 3> descriptor_sets = {};
|
||||
std::vector<vk::UniqueDescriptorSet> descriptor_sets = {};
|
||||
vk::UniqueDescriptorPool desc_pool;
|
||||
|
||||
// Rasterization
|
||||
vk::Viewport viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
||||
vk::Viewport viewport = { 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f };
|
||||
vk::CullModeFlags cull_mode = vk::CullModeFlagBits::eNone;
|
||||
vk::Rect2D scissor = {{0, 0}, {1, 1}};
|
||||
vk::Rect2D scissor = { {0, 0}, {1, 1} };
|
||||
VKTexture dummy_texture;
|
||||
|
||||
// Framebuffer
|
||||
VKFramebuffer* framebuffer = nullptr;
|
||||
vk::RenderPass current_render_pass = VK_NULL_HANDLE;
|
||||
vk::Rect2D framebuffer_render_area = {};
|
||||
// Render attachments
|
||||
VKTexture* color_attachment = nullptr, * depth_attachment = nullptr;
|
||||
vk::Rect2D render_area = {};
|
||||
vk::ColorComponentFlags color_mask;
|
||||
|
||||
// Depth
|
||||
|
@ -51,11 +51,8 @@ void VKTexture::Create(const Info& info, bool make_staging) {
|
||||
LOG_CRITICAL(Render_Vulkan, "Unknown texture format {}", texture_info.format);
|
||||
}
|
||||
|
||||
// Make sure the texture size doesn't exceed the global staging buffer size
|
||||
u32 image_size = texture_info.width * texture_info.height * channels;
|
||||
assert(image_size <= MAX_TEXTURE_UPLOAD_BUFFER_SIZE);
|
||||
|
||||
// Create the texture
|
||||
u32 image_size = texture_info.width * texture_info.height * channels;
|
||||
vk::ImageCreateFlags flags;
|
||||
if (info.view_type == vk::ImageViewType::eCube) {
|
||||
flags = vk::ImageCreateFlagBits::eCubeCompatible;
|
||||
@ -103,7 +100,7 @@ void VKTexture::Adopt(vk::Image image, vk::ImageViewCreateInfo view_info) {
|
||||
texture_view = g_vk_instace->GetDevice().createImageView(view_info);
|
||||
}
|
||||
|
||||
void VKTexture::TransitionLayout(vk::ImageLayout new_layout, vk::CommandBuffer& command_buffer) {
|
||||
void VKTexture::TransitionLayout(vk::ImageLayout new_layout, vk::CommandBuffer command_buffer) {
|
||||
struct LayoutInfo {
|
||||
vk::ImageLayout layout;
|
||||
vk::AccessFlags access;
|
||||
@ -209,12 +206,13 @@ void VKTexture::CopyPixels(std::span<u32> new_pixels) {
|
||||
TransitionLayout(vk::ImageLayout::eShaderReadOnlyOptimal, command_buffer);
|
||||
}
|
||||
|
||||
void VKTexture::BlitTo(Common::Rectangle<u32> srect, VKTexture& dest,
|
||||
Common::Rectangle<u32> drect, SurfaceParams::SurfaceType type,
|
||||
vk::CommandBuffer& command_buffer) {
|
||||
void VKTexture::BlitTo(Common::Rectangle<u32> srect, VKTexture* dest,
|
||||
Common::Rectangle<u32> drect, SurfaceParams::SurfaceType type) {
|
||||
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||
|
||||
// Ensure textures are of the same dimention
|
||||
assert(texture_info.width == dest.texture_info.width &&
|
||||
texture_info.height == dest.texture_info.height);
|
||||
assert(texture_info.width == dest->texture_info.width &&
|
||||
texture_info.height == dest->texture_info.height);
|
||||
|
||||
vk::ImageAspectFlags image_aspect;
|
||||
switch (type) {
|
||||
@ -242,73 +240,34 @@ void VKTexture::BlitTo(Common::Rectangle<u32> srect, VKTexture& dest,
|
||||
|
||||
// Transition image layouts
|
||||
TransitionLayout(vk::ImageLayout::eTransferSrcOptimal, command_buffer);
|
||||
dest.TransitionLayout(vk::ImageLayout::eTransferDstOptimal, command_buffer);
|
||||
dest->TransitionLayout(vk::ImageLayout::eTransferDstOptimal, command_buffer);
|
||||
|
||||
// Perform blit operation
|
||||
command_buffer.blitImage(texture, vk::ImageLayout::eTransferSrcOptimal, dest.texture,
|
||||
command_buffer.blitImage(texture, vk::ImageLayout::eTransferSrcOptimal, dest->GetHandle(),
|
||||
vk::ImageLayout::eTransferDstOptimal, regions, vk::Filter::eNearest);
|
||||
}
|
||||
|
||||
void VKTexture::Fill(Common::Rectangle<u32> region, glm::vec4 color) {
|
||||
void VKTexture::Fill(Common::Rectangle<u32> region, vk::ImageAspectFlags aspect,
|
||||
vk::ClearValue value) {
|
||||
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||
TransitionLayout(vk::ImageLayout::eTransferDstOptimal, command_buffer);
|
||||
|
||||
}
|
||||
// End any ongoing rendering operations
|
||||
g_vk_state->EndRendering();
|
||||
|
||||
void VKTexture::Fill(Common::Rectangle<u32> region, glm::vec2 depth_stencil) {
|
||||
// Set fill area
|
||||
g_vk_state->SetAttachments(this, nullptr);
|
||||
|
||||
}
|
||||
// Begin clear render
|
||||
g_vk_state->BeginRendering();
|
||||
|
||||
VKFramebuffer::~VKFramebuffer() {
|
||||
auto deleter = [this]() {
|
||||
if (framebuffer) {
|
||||
auto& device = g_vk_instace->GetDevice();
|
||||
device.destroyFramebuffer(framebuffer);
|
||||
}
|
||||
};
|
||||
vk::Offset2D offset(region.left, region.bottom);
|
||||
vk::Rect2D rect(offset, { region.GetWidth(), region.GetHeight() });
|
||||
vk::ClearAttachment clear_info(aspect, 0, value);
|
||||
vk::ClearRect clear_rect(rect, 0, 1);
|
||||
command_buffer.clearAttachments(clear_info, clear_rect);
|
||||
|
||||
g_vk_task_scheduler->Schedule(deleter);
|
||||
}
|
||||
|
||||
void VKFramebuffer::Create(const Info& info) {
|
||||
// Make sure that either attachment is valid
|
||||
assert(info.color || info.depth_stencil);
|
||||
attachments = { info.color, info.depth_stencil };
|
||||
|
||||
auto rect = info.color ? info.color->GetRect() : info.depth_stencil->GetRect();
|
||||
auto color_format = info.color ? info.color->GetFormat() : vk::Format::eUndefined;
|
||||
auto depth_format = info.depth_stencil ? info.depth_stencil->GetFormat() : vk::Format::eUndefined;
|
||||
|
||||
vk::FramebufferCreateInfo framebuffer_info
|
||||
(
|
||||
{},
|
||||
g_vk_res_cache->GetRenderPass(color_format, depth_format, 1, vk::AttachmentLoadOp::eLoad),
|
||||
{},
|
||||
rect.extent.width,
|
||||
rect.extent.height,
|
||||
1
|
||||
);
|
||||
|
||||
if (info.color && info.depth_stencil) {
|
||||
std::array<vk::ImageView, 2> views = { info.color->GetView(), info.depth_stencil->GetView() };
|
||||
framebuffer_info.setAttachments(views);
|
||||
}
|
||||
else {
|
||||
auto valid = info.color ? info.color : info.depth_stencil;
|
||||
std::array<vk::ImageView, 1> view = { valid->GetView() };
|
||||
framebuffer_info.setAttachments(view);
|
||||
}
|
||||
|
||||
framebuffer = g_vk_instace->GetDevice().createFramebuffer(framebuffer_info);
|
||||
}
|
||||
|
||||
void VKFramebuffer::Prepare(vk::CommandBuffer& command_buffer) {
|
||||
// Transition attachments to their optimal formats for rendering
|
||||
if (attachments[Attachments::Color]) {
|
||||
attachments[Attachments::Color]->TransitionLayout(vk::ImageLayout::eColorAttachmentOptimal, command_buffer);
|
||||
}
|
||||
|
||||
if (attachments[Attachments::DepthStencil]) {
|
||||
attachments[Attachments::DepthStencil]->TransitionLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal, command_buffer);
|
||||
}
|
||||
TransitionLayout(vk::ImageLayout::eShaderReadOnlyOptimal, command_buffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -52,24 +52,24 @@ public:
|
||||
void CopyPixels(std::span<u32> pixels);
|
||||
|
||||
/// Get Vulkan objects
|
||||
vk::Image GetImage() const { return texture; }
|
||||
vk::Image GetHandle() const { return texture; }
|
||||
vk::ImageView GetView() const { return texture_view; }
|
||||
vk::Format GetFormat() const { return texture_info.format; }
|
||||
vk::Rect2D GetRect() const { return vk::Rect2D({}, { texture_info.width, texture_info.height }); }
|
||||
vk::ImageLayout GetLayout() const { return texture_layout; }
|
||||
u32 GetSamples() const { return texture_info.multisamples; }
|
||||
bool IsValid() { return texture; }
|
||||
|
||||
/// Used to transition the image to an optimal layout during transfers
|
||||
void TransitionLayout(vk::ImageLayout new_layout, vk::CommandBuffer& command_buffer);
|
||||
void TransitionLayout(vk::ImageLayout new_layout, vk::CommandBuffer command_buffer);
|
||||
|
||||
/// Fill the texture with the values provided
|
||||
void Fill(Common::Rectangle<u32> region, glm::vec4 color);
|
||||
void Fill(Common::Rectangle<u32> region, glm::vec2 depth_stencil);
|
||||
void Fill(Common::Rectangle<u32> region, vk::ImageAspectFlags aspect,
|
||||
vk::ClearValue value);
|
||||
|
||||
/// Copy current texture to another with optionally performing format convesions
|
||||
void BlitTo(Common::Rectangle<u32> source_rect, VKTexture& dest,
|
||||
Common::Rectangle<u32> dst_rect, SurfaceParams::SurfaceType type,
|
||||
vk::CommandBuffer& command_buffer);
|
||||
void BlitTo(Common::Rectangle<u32> srect, VKTexture* dest,
|
||||
Common::Rectangle<u32> drect, SurfaceParams::SurfaceType type);
|
||||
|
||||
private:
|
||||
bool cleanup_image = true;
|
||||
@ -84,34 +84,4 @@ private:
|
||||
VKBuffer staging;
|
||||
};
|
||||
|
||||
enum Attachments {
|
||||
Color = 0,
|
||||
DepthStencil = 1
|
||||
};
|
||||
|
||||
/// Vulkan framebuffer object similar to an FBO in OpenGL
|
||||
class VKFramebuffer final : public NonCopyable {
|
||||
public:
|
||||
struct Info {
|
||||
VKTexture* color = nullptr;
|
||||
VKTexture* depth_stencil = nullptr;
|
||||
};
|
||||
|
||||
VKFramebuffer() = default;
|
||||
~VKFramebuffer();
|
||||
|
||||
/// Create Vulkan framebuffer object
|
||||
void Create(const Info& info);
|
||||
|
||||
/// Configure frambuffer for rendering
|
||||
void Prepare(vk::CommandBuffer& command_buffer);
|
||||
|
||||
vk::Rect2D GetRect() const { return vk::Rect2D({}, { width, height }); }
|
||||
|
||||
private:
|
||||
u32 width, height;
|
||||
vk::Framebuffer framebuffer;
|
||||
std::array<VKTexture*, 2> attachments;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
Reference in New Issue
Block a user