From ba6795b6cc39111723179e9681adb36e5777f8b9 Mon Sep 17 00:00:00 2001 From: emufan4568 Date: Wed, 8 Jun 2022 00:40:27 +0300 Subject: [PATCH] Refactor Vulkan state and pipeline builders --- .../renderer_vulkan/renderer_vulkan.cpp | 518 ++++++------------ .../renderer_vulkan/renderer_vulkan.h | 40 +- src/video_core/renderer_vulkan/vk_buffer.cpp | 40 +- src/video_core/renderer_vulkan/vk_buffer.h | 16 +- .../vk_format_reinterpreter.cpp | 384 +------------ .../renderer_vulkan/vk_format_reinterpreter.h | 2 +- src/video_core/renderer_vulkan/vk_instance.h | 3 +- .../renderer_vulkan/vk_pipeline_builder.cpp | 136 ++++- .../renderer_vulkan/vk_pipeline_builder.h | 38 +- .../renderer_vulkan/vk_rasterizer.cpp | 26 +- .../renderer_vulkan/vk_rasterizer.h | 3 - src/video_core/renderer_vulkan/vk_state.cpp | 192 +++---- src/video_core/renderer_vulkan/vk_state.h | 80 ++- .../renderer_vulkan/vk_swapchain.cpp | 14 +- .../renderer_vulkan/vk_task_scheduler.cpp | 50 +- .../renderer_vulkan/vk_task_scheduler.h | 25 +- src/video_core/renderer_vulkan/vk_texture.cpp | 86 +-- src/video_core/renderer_vulkan/vk_texture.h | 12 +- 18 files changed, 626 insertions(+), 1039 deletions(-) diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index e32766207..7b4fdcbc7 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -32,6 +32,7 @@ #include "video_core/renderer_vulkan/vk_state.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/vk_task_scheduler.h" +#include "video_core/renderer_vulkan/vk_pipeline_builder.h" #include "video_core/video_core.h" // Include these late to avoid polluting previous headers @@ -129,17 +130,17 @@ std::vector RequiredExtensions(Frontend::WindowSystemType window_ty return extensions; } -static const char vertex_shader[] = R"( -in vec2 vert_position; -in vec2 vert_tex_coord; -out vec2 frag_tex_coord; +static const char vertex_shader_source[] = R"( +layout (location = 0) in vec2 vert_position; +layout (location = 1) in vec2 vert_tex_coord; +layout (location = 0) out vec2 frag_tex_coord; // This is a truncated 3x3 matrix for 2D transformations: // The upper-left 2x2 submatrix performs scaling/rotation/mirroring. // The third column performs translation. // The third row could be used for projection, which we don't need in 2D. It hence is assumed to // implicitly be [0, 0, 1] -uniform mat3x2 modelview_matrix; +layout (push_constant) uniform mat3x2 modelview_matrix; void main() { // Multiply input position by the rotscale part of the matrix and then manually translate by @@ -150,13 +151,15 @@ void main() { } )"; -static const char fragment_shader[] = R"( -in vec2 frag_tex_coord; -layout(location = 0) out vec4 color; +static const char fragment_shader_source[] = R"( +layout (location = 0) in vec2 frag_tex_coord; +layout (location = 0) out vec4 color; -uniform vec4 i_resolution; -uniform vec4 o_resolution; -uniform int layer; +layout (push_constant) uniform DrawInfo { + vec4 i_resolution; + vec4 o_resolution; + int layer; +}; uniform sampler2D color_texture; @@ -165,70 +168,32 @@ void main() { } )"; -static const char fragment_shader_anaglyph[] = R"( - -// Anaglyph Red-Cyan shader based on Dubois algorithm -// Constants taken from the paper: -// "Conversion of a Stereo Pair to Anaglyph with -// the Least-Squares Projection Method" -// Eric Dubois, March 2009 -const mat3 l = mat3( 0.437, 0.449, 0.164, - -0.062,-0.062,-0.024, - -0.048,-0.050,-0.017); -const mat3 r = mat3(-0.011,-0.032,-0.007, - 0.377, 0.761, 0.009, - -0.026,-0.093, 1.234); - -in vec2 frag_tex_coord; -out vec4 color; - -uniform vec4 resolution; -uniform int layer; - -uniform sampler2D color_texture; -uniform sampler2D color_texture_r; - -void main() { - vec4 color_tex_l = texture(color_texture, frag_tex_coord); - vec4 color_tex_r = texture(color_texture_r, frag_tex_coord); - color = vec4(color_tex_l.rgb*l+color_tex_r.rgb*r, color_tex_l.a); -} -)"; - -static const char fragment_shader_interlaced[] = R"( - -in vec2 frag_tex_coord; -out vec4 color; - -uniform vec4 o_resolution; - -uniform sampler2D color_texture; -uniform sampler2D color_texture_r; - -uniform int reverse_interlaced; - -void main() { - float screen_row = o_resolution.x * frag_tex_coord.x; - if (int(screen_row) % 2 == reverse_interlaced) - color = texture(color_texture, frag_tex_coord); - else - color = texture(color_texture_r, frag_tex_coord); -} -)"; - /** * Vertex structure that the drawn screen rectangles are composed of. */ -struct ScreenRectVertex { - ScreenRectVertex(GLfloat x, GLfloat y, GLfloat u, GLfloat v) { - position[0] = x; - position[1] = y; - tex_coord[0] = u; - tex_coord[1] = v; + +struct ScreenRectVertexBase { + ScreenRectVertexBase() = default; + ScreenRectVertexBase(float x, float y, float u, float v) { + position.x = x; + position.y = y; + tex_coord.x = u; + tex_coord.y = v; } - GLfloat position[2]; - GLfloat tex_coord[2]; + glm::vec2 position; + glm::vec2 tex_coord; +}; + +struct ScreenRectVertex : public ScreenRectVertexBase { + ScreenRectVertex() = default; + ScreenRectVertex(float x, float y, float u, float v) : ScreenRectVertexBase(x, y, u, v) {}; + static constexpr auto binding_desc = vk::VertexInputBindingDescription(0, sizeof(ScreenRectVertexBase)); + static constexpr std::array attribute_desc = + { + vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32Sfloat, offsetof(ScreenRectVertexBase, position)), + vk::VertexInputAttributeDescription(1, 0, vk::Format::eR32G32Sfloat, offsetof(ScreenRectVertexBase, tex_coord)), + }; }; /** @@ -273,14 +238,10 @@ MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128 /// Swap buffers (render frame) void RendererVulkan::SwapBuffers() { - // Maintain the rasterizer's state as a priority - OpenGLState prev_state = OpenGLState::GetCurState(); - state.Apply(); - PrepareRendertarget(); const auto& layout = render_window.GetFramebufferLayout(); - RenderToMailbox(layout, render_window.mailbox, false); + DrawScreens(layout, false); m_current_frame++; @@ -292,7 +253,6 @@ void RendererVulkan::SwapBuffers() { Core::System::GetInstance().CoreTiming().GetGlobalTimeUs()); Core::System::GetInstance().perf_stats->BeginSystemFrame(); - prev_state.Apply(); RefreshRasterizerSetting(); if (Pica::g_debug_context && Pica::g_debug_context->recorder) { @@ -315,9 +275,9 @@ void RendererVulkan::PrepareRendertarget() { if (color_fill.is_enabled) { LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, screen_infos[i]); } else { - auto rect = screen_infos[i].texture->GetExtent(); + auto extent = screen_infos[i].texture.GetArea().extent; auto format = screen_infos[i].format; - if (rect.width != framebuffer.width || rect.height != framebuffer.height || + if (extent.width != framebuffer.width || extent.height != framebuffer.height || format != framebuffer.color_format) { // Reallocate texture if the framebuffer size has changed. // This is expected to not happen very often and hence should not be a @@ -328,40 +288,12 @@ void RendererVulkan::PrepareRendertarget() { LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1); // Resize the texture in case the framebuffer size has changed - screen_infos[i].texture.width = framebuffer.width; - screen_infos[i].texture.height = framebuffer.height; + //screen_infos[i].texture.width = framebuffer.width; + //screen_infos[i].texture.height = framebuffer.height; } } } -void RendererVulkan::RenderToMailbox(const Layout::FramebufferLayout& layout, - std::unique_ptr& mailbox, - bool flipped) { - - Frontend::Frame* frame; - { - MICROPROFILE_SCOPE(OpenGL_WaitPresent); - - frame = mailbox->GetRenderFrame(); - } - - { - MICROPROFILE_SCOPE(OpenGL_RenderFrame); - // Recreate the frame if the size of the window has changed - if (layout.width != frame->width || layout.height != frame->height) { - LOG_DEBUG(Render_OpenGL, "Reloading render frame"); - mailbox->ReloadRenderFrame(frame, layout.width, layout.height); - } - - GLuint render_texture = frame->color.handle; - state.draw.draw_framebuffer = frame->render.handle; - state.Apply(); - DrawScreens(layout, flipped); - // Create a fence for the frontend to wait on and swap this frame to OffTex - mailbox->ReleaseRenderFrame(frame); - } -} - /** * Loads framebuffer from emulated memory into the active OpenGL texture. */ @@ -392,16 +324,16 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram if (!Rasterizer()->AccelerateDisplay(framebuffer, framebuffer_addr, static_cast(pixel_stride), screen_info)) { // Reset the screen info's display texture to its own permanent texture - screen_info.display_texture = screen_info.texture; + screen_info.display_texture = screen_info.texture.GetHandle(); screen_info.display_texcoords = Common::Rectangle(0.f, 0.f, 1.f, 1.f); Memory::RasterizerFlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height); vk::Rect2D region{{0, 0}, {framebuffer.width, framebuffer.height}}; std::span framebuffer_data(VideoCore::g_memory->GetPhysicalPointer(framebuffer_addr), - screen_info.texture->GetSize()); + screen_info.texture.GetSize()); - screen_info.texture->Upload(0, 1, pixel_stride, region, framebuffer_data); + screen_info.texture.Upload(0, 1, pixel_stride, region, framebuffer_data); } } @@ -409,9 +341,8 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram * Fills active OpenGL texture with the given RGB color. Since the color is solid, the texture can * be 1x1 but will stretch across whatever it's rendered on. */ -void RendererVulkan::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, - const ScreenInfo& screen) { - state.texture_units[0].texture_2d = texture.resource.handle; +void RendererVulkan::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const ScreenInfo& screen) { + /*state.texture_units[0].texture_2d = texture.resource.handle; state.Apply(); glActiveTexture(GL_TEXTURE0); @@ -421,161 +352,105 @@ void RendererVulkan::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer_data); state.texture_units[0].texture_2d = 0; - state.Apply(); + state.Apply();*/ } /** * Initializes the OpenGL state and creates persistent objects. */ -void RendererVulkan::InitOpenGLObjects() { +void RendererVulkan::CreateVulkanObjects() { glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); - filter_sampler.Create(); - ReloadSampler(); + //filter_sampler.Create(); + //ReloadSampler(); ReloadShader(); // Generate VBO handle for drawing - vertex_buffer.Create(); + VKBuffer::Info vertex_info{ + .size = sizeof(ScreenRectVertex) * 4, + .properties = vk::MemoryPropertyFlagBits::eDeviceLocal, + .usage = vk::BufferUsageFlagBits::eVertexBuffer | + vk::BufferUsageFlagBits::eTransferDst + }; + vertex_buffer.Create(vertex_info); +} - // Generate VAO - vertex_array.Create(); +void RendererVulkan::ConfigureRenderPipeline() { + // Define the descriptor sets we will be using + vk::DescriptorSetLayoutBinding color_texture{ + 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment + }; + vk::DescriptorSetLayoutCreateInfo color_texture_info{{}, color_texture}; - state.draw.vertex_array = vertex_array.handle; - state.draw.vertex_buffer = vertex_buffer.handle; - state.draw.uniform_buffer = 0; - state.Apply(); + auto& device = g_vk_instace->GetDevice(); + descriptor_layout = device.createDescriptorSetLayoutUnique(color_texture_info); - // Attach vertex data to VAO - glBufferData(GL_ARRAY_BUFFER, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); - glVertexAttribPointer(attrib_position, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), - (GLvoid*)offsetof(ScreenRectVertex, position)); - glVertexAttribPointer(attrib_tex_coord, 2, GL_FLOAT, GL_FALSE, sizeof(ScreenRectVertex), - (GLvoid*)offsetof(ScreenRectVertex, tex_coord)); - glEnableVertexAttribArray(attrib_position); - glEnableVertexAttribArray(attrib_tex_coord); + // Build the display pipeline layout + PipelineLayoutBuilder lbuilder; + lbuilder.AddDescriptorSet(descriptor_layout.get()); + lbuilder.AddPushConstants(vk::ShaderStageFlagBits::eVertex, 0, sizeof(glm::mat2x3)); + lbuilder.AddPushConstants(vk::ShaderStageFlagBits::eFragment, 0, sizeof(DrawInfo)); + pipeline_layout = vk::UniquePipelineLayout{lbuilder.Build()}; - // Allocate textures for each screen - for (auto& screen_info : screen_infos) { - screen_info.texture.resource.Create(); + std::array dynamic_states{ + vk::DynamicState::eLineWidth, vk::DynamicState::eViewport, vk::DynamicState::eScissor, + }; - // Allocation of storage is deferred until the first frame, when we - // know the framebuffer size. + // Build the display pipeline + PipelineBuilder builder; + builder.SetNoStencilState(); + builder.SetNoBlendingState(); + builder.SetNoDepthTestState(); + builder.SetNoCullRasterizationState(); + builder.SetLineWidth(1.0f); + builder.SetPrimitiveTopology(vk::PrimitiveTopology::eTriangleList); + builder.SetShaderStage(vk::ShaderStageFlagBits::eVertex, vertex_shader.get()); + builder.SetShaderStage(vk::ShaderStageFlagBits::eFragment, fragment_shader.get()); + builder.SetDynamicStates(dynamic_states); + builder.SetPipelineLayout(pipeline_layout.get()); - state.texture_units[0].texture_2d = screen_info.texture.resource.handle; - state.Apply(); + // Configure vertex buffer + auto attributes = ScreenRectVertex::attribute_desc; + builder.AddVertexBuffer(0, sizeof(ScreenRectVertex), vk::VertexInputRate::eVertex, attributes); - glActiveTexture(GL_TEXTURE0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - screen_info.display_texture = screen_info.texture.resource.handle; - } - - state.texture_units[0].texture_2d = 0; - state.Apply(); + pipeline = vk::UniquePipeline{builder.Build()}; } void RendererVulkan::ReloadSampler() { - glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MIN_FILTER, + /*glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MIN_FILTER, Settings::values.filter_mode ? GL_LINEAR : GL_NEAREST); glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_MAG_FILTER, Settings::values.filter_mode ? GL_LINEAR : GL_NEAREST); glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glSamplerParameteri(filter_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);*/ } void RendererVulkan::ReloadShader() { // Link shaders and get variable locations - std::string shader_data = fragment_shader; - /*if (GLES) { - shader_data += fragment_shader_precision_OES; - } - if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph) { - if (Settings::values.pp_shader_name == "dubois (builtin)") { - shader_data += fragment_shader_anaglyph; - } else { - std::string shader_text = - OpenGL::GetPostProcessingShaderCode(true, Settings::values.pp_shader_name); - if (shader_text.empty()) { - // Should probably provide some information that the shader couldn't load - shader_data += fragment_shader_anaglyph; - } else { - shader_data += shader_text; - } - } - } else if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) { - if (Settings::values.pp_shader_name == "horizontal (builtin)") { - shader_data += fragment_shader_interlaced; - } else { - std::string shader_text = - OpenGL::GetPostProcessingShaderCode(true, Settings::values.pp_shader_name); - if (shader_text.empty()) { - // Should probably provide some information that the shader couldn't load - shader_data += fragment_shader_interlaced; - } else { - shader_data += shader_text; - } - } - } else { - if (Settings::values.pp_shader_name == "none (builtin)") { - shader_data += fragment_shader; - } else { - std::string shader_text = - OpenGL::GetPostProcessingShaderCode(false, Settings::values.pp_shader_name); - if (shader_text.empty()) { - // Should probably provide some information that the shader couldn't load - shader_data += fragment_shader; - } else { - shader_data += shader_text; - } - } - }*/ - shader.Create(vertex_shader, shader_data.c_str()); - state.draw.shader_program = shader.handle; - state.Apply(); - uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix"); - uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture"); - if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph || - Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) { - uniform_color_texture_r = glGetUniformLocation(shader.handle, "color_texture_r"); - } - if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) { - GLuint uniform_reverse_interlaced = - glGetUniformLocation(shader.handle, "reverse_interlaced"); - if (Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) - glUniform1i(uniform_reverse_interlaced, 1); - else - glUniform1i(uniform_reverse_interlaced, 0); - } - uniform_i_resolution = glGetUniformLocation(shader.handle, "i_resolution"); - uniform_o_resolution = glGetUniformLocation(shader.handle, "o_resolution"); - uniform_layer = glGetUniformLocation(shader.handle, "layer"); - attrib_position = glGetAttribLocation(shader.handle, "vert_position"); - attrib_tex_coord = glGetAttribLocation(shader.handle, "vert_tex_coord"); + vertex_shader = vk::UniqueShaderModule{VulkanState::CompileShader(vertex_shader_source, + vk::ShaderStageFlagBits::eVertex)}; + fragment_shader = vk::UniqueShaderModule{VulkanState::CompileShader(fragment_shader_source, + vk::ShaderStageFlagBits::eFragment)}; } -void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture, - const GPU::Regs::FramebufferConfig& framebuffer) { +void RendererVulkan::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::Regs::FramebufferConfig& framebuffer) { GPU::Regs::PixelFormat format = framebuffer.color_format; - GLint internal_format; - texture.format = format; - texture.width = framebuffer.width; - texture.height = framebuffer.height; + VKTexture::Info texture_info{ + .width = framebuffer.width, + .height = framebuffer.height, + .type = vk::ImageType::e2D, + .view_type = vk::ImageViewType::e2D, + .usage = vk::ImageUsageFlagBits::eColorAttachment | + vk::ImageUsageFlagBits::eTransferDst, + .aspect = vk::ImageAspectFlagBits::eColor, + }; switch (format) { case GPU::Regs::PixelFormat::RGBA8: - internal_format = GL_RGBA; - texture.gl_format = GL_RGBA; - texture.gl_type = GLES ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_8_8_8_8; + texture_info.format = vk::Format::eR8G8B8A8Unorm; break; case GPU::Regs::PixelFormat::RGB8: @@ -583,44 +458,33 @@ void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture, // specific OpenGL type used in this function using native-endian (that is, little-endian // mostly everywhere) for words or half-words. // TODO: check how those behave on big-endian processors. - internal_format = GL_RGB; + //internal_format = GL_RGB; // GLES Dosen't support BGR , Use RGB instead - texture.gl_format = GLES ? GL_RGB : GL_BGR; - texture.gl_type = GL_UNSIGNED_BYTE; + //texture.gl_format = GLES ? GL_RGB : GL_BGR; + //texture.gl_type = GL_UNSIGNED_BYTE; + texture_info.format = vk::Format::eR8G8B8Unorm; break; case GPU::Regs::PixelFormat::RGB565: - internal_format = GL_RGB; - texture.gl_format = GL_RGB; - texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; + texture_info.format = vk::Format::eR5G6B5UnormPack16; break; case GPU::Regs::PixelFormat::RGB5A1: - internal_format = GL_RGBA; - texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_SHORT_5_5_5_1; + texture_info.format = vk::Format::eR5G5B5A1UnormPack16; break; case GPU::Regs::PixelFormat::RGBA4: - internal_format = GL_RGBA; - texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_SHORT_4_4_4_4; + texture_info.format = vk::Format::eR4G4B4A4UnormPack16; break; default: UNIMPLEMENTED(); } - state.texture_units[0].texture_2d = texture.resource.handle; - state.Apply(); - - glActiveTexture(GL_TEXTURE0); - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, - texture.gl_format, texture.gl_type, nullptr); - - state.texture_units[0].texture_2d = 0; - state.Apply(); + auto& texture = screen.texture; + texture.Destroy(); + texture.Create(texture_info); } /** @@ -638,15 +502,21 @@ void RendererVulkan::DrawSingleScreenRotated(const ScreenInfo& screen_info, floa ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.right), }}; + auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); + // As this is the "DrawSingleScreenRotated" function, the output resolution dimensions have been // swapped. If a non-rotated draw-screen function were to be added for book-mode games, those // should probably be set to the standard (w, h, 1.0 / w, 1.0 / h) ordering. const u16 scale_factor = VideoCore::GetResolutionScaleFactor(); - glUniform4f(uniform_i_resolution, static_cast(screen_info.texture.width * scale_factor), - static_cast(screen_info.texture.height * scale_factor), - 1.0f / static_cast(screen_info.texture.width * scale_factor), - 1.0f / static_cast(screen_info.texture.height * scale_factor)); - glUniform4f(uniform_o_resolution, h, w, 1.0f / h, 1.0f / w); + auto [width, height] = screen_info.texture.GetArea().extent; + + draw_info.i_resolution = glm::vec4(width * scale_factor, height * scale_factor, + 1.0f / (width * scale_factor), + 1.0f / (height * scale_factor)); + draw_info.o_resolution = glm::vec4(h, w, 1.0f / h, 1.0f / w); + command_buffer.pushConstants(pipeline_layout.get(), vk::ShaderStageFlagBits::eFragment, + 0, sizeof(DrawInfo), &draw_info); + state.texture_units[0].texture_2d = screen_info.display_texture; state.texture_units[0].sampler = filter_sampler.handle; state.Apply(); @@ -776,12 +646,12 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f ReloadSampler(); } - if (VideoCore::g_renderer_shader_update_requested.exchange(false)) { + /*if (VideoCore::g_renderer_shader_update_requested.exchange(false)) { // Update fragment shader before drawing shader.Release(); // Link shaders and get variable locations ReloadShader(); - } + }*/ const auto& top_screen = layout.top_screen; const auto& bottom_screen = layout.bottom_screen; @@ -930,105 +800,42 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f } } -void RendererVulkan::TryPresent(int timeout_ms) { +bool RendererVulkan::BeginPresent() { + // Previous frame needs to be presented before we can acquire the swap chain. + g_vulkan_context->WaitForPresentComplete(); - g_vk_task_scheduler->Submit(true); + auto available = swapchain->AcquireNextImage(); + auto cmdbuffer = g_vk_task_scheduler->GetCommandBuffer(); - const auto& layout = render_window.GetFramebufferLayout(); - auto frame = render_window.mailbox->TryGetPresentFrame(timeout_ms); - if (!frame) { - LOG_DEBUG(Render_Vulkan, "TryGetPresentFrame returned no frame to present"); - return; - } + // Swap chain images start in undefined + Vulkan::Texture& swap_chain_texture = m_swap_chain->GetCurrentTexture(); + swap_chain_texture.OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED); + swap_chain_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a - // readback since we won't be doing any blending - glClear(GL_COLOR_BUFFER_BIT); + const VkClearValue clear_value = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, m_swap_chain->GetClearRenderPass(), + m_swap_chain->GetCurrentFramebuffer(), {{0, 0}, {swap_chain_texture.GetWidth(), swap_chain_texture.GetHeight()}}, 1u, &clear_value}; + vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE); - // Recreate the presentation FBO if the color attachment was changed - if (frame->color_reloaded) { - LOG_DEBUG(Render_Vulkan, "Reloading present frame"); - render_window.mailbox->ReloadPresentFrame(frame, layout.width, layout.height); - } - - glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED); - // INTEL workaround. - // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete - // it on the emulation thread without too much penalty - // glDeleteSync(frame.render_sync); - // frame.render_sync = 0; - - glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle); - glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height, - GL_COLOR_BUFFER_BIT, GL_LINEAR); - - // Delete the fence if we're re-presenting to avoid leaking fences - if (frame->present_fence) { - glDeleteSync(frame->present_fence); - } - - /* insert fence for the main thread to block on */ - frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glFlush(); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + const VkViewport vp{ + 0.0f, 0.0f, static_cast(swap_chain_texture.GetWidth()), static_cast(swap_chain_texture.GetHeight()), 0.0f, 1.0f}; + const VkRect2D scissor{{0, 0}, {static_cast(swap_chain_texture.GetWidth()), static_cast(swap_chain_texture.GetHeight())}}; + vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp); + vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor); + return true; } -/// Updates the framerate -void RendererVulkan::UpdateFramerate() {} +void RendererVulkan::EndPresent() { + ImGui::Render(); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData()); -static const char* GetSource(GLenum source) { -#define RET(s) \ - case GL_DEBUG_SOURCE_##s: \ - return #s - switch (source) { - RET(API); - RET(WINDOW_SYSTEM); - RET(SHADER_COMPILER); - RET(THIRD_PARTY); - RET(APPLICATION); - RET(OTHER); - default: - UNREACHABLE(); - } -#undef RET -} + VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer(); + vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer()); + m_swap_chain->GetCurrentTexture().TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); -static const char* GetType(GLenum type) { -#define RET(t) \ - case GL_DEBUG_TYPE_##t: \ - return #t - switch (type) { - RET(ERROR); - RET(DEPRECATED_BEHAVIOR); - RET(UNDEFINED_BEHAVIOR); - RET(PORTABILITY); - RET(PERFORMANCE); - RET(OTHER); - RET(MARKER); - default: - UNREACHABLE(); - } -#undef RET -} - -static void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, - GLsizei length, const GLchar* message, const void* user_param) { - Log::Level level; - switch (severity) { - case GL_DEBUG_SEVERITY_HIGH: - level = Log::Level::Critical; - break; - case GL_DEBUG_SEVERITY_MEDIUM: - level = Log::Level::Warning; - break; - case GL_DEBUG_SEVERITY_NOTIFICATION: - case GL_DEBUG_SEVERITY_LOW: - level = Log::Level::Debug; - break; - } - LOG_GENERIC(Log::Class::Render_OpenGL, level, "{} {} {}: {}", GetSource(source), GetType(type), - id, message); + g_vulkan_context->SubmitCommandBuffer(m_swap_chain->GetImageAvailableSemaphore(), m_swap_chain->GetRenderingFinishedSemaphore(), + m_swap_chain->GetSwapChain(), m_swap_chain->GetCurrentImageIndex(), !m_swap_chain->IsPresentModeSynchronizing()); + g_vulkan_context->MoveToNextCommandBuffer(); } /// Initialize the renderer @@ -1050,14 +857,19 @@ VideoCore::ResultStatus RendererVulkan::Init() { g_vk_instace = std::make_unique(); g_vk_instace->Create(instance, physical_device, surface, true); + // Create Vulkan state and task manager + VulkanState::Create(); + g_vk_task_scheduler = std::make_unique(); + g_vk_task_scheduler->Create(); + auto& telemetry_session = Core::System::GetInstance().TelemetrySession(); constexpr auto user_system = Common::Telemetry::FieldType::UserSystem; telemetry_session.AddField(user_system, "GPU_Vendor", "NVIDIA"); telemetry_session.AddField(user_system, "GPU_Model", "GTX 1650"); telemetry_session.AddField(user_system, "GPU_Vulkan_Version", "Vulkan 1.3"); - InitOpenGLObjects(); - + // Initialize the renderer + CreateVulkanObjects(); RefreshRasterizerSetting(); return VideoCore::ResultStatus::Success; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 273e740f5..662cf7fcf 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -30,12 +30,18 @@ namespace Vulkan { /// Structure used for storing information about the display target for each 3DS screen struct ScreenInfo { - Vulkan::VKTexture* display_texture; + vk::Image display_texture; Common::Rectangle display_texcoords; - Vulkan::VKTexture* texture; + Vulkan::VKTexture texture; GPU::Regs::PixelFormat format; }; +struct DrawInfo { + glm::vec4 i_resolution; + glm::vec4 o_resolution; + int layer; +}; + class RendererVulkan : public RendererBase { public: RendererVulkan(Frontend::EmuWindow& window); @@ -47,29 +53,25 @@ public: /// Shutdown the renderer void ShutDown() override; - /// Finalizes rendering the guest frame - void SwapBuffers() override; - - /// Draws the latest frame from texture mailbox to the currently bound draw framebuffer in this - /// context - void TryPresent(int timeout_ms) override; + bool BeginPresent(); + void EndPresent(); private: - void InitOpenGLObjects(); + void CreateVulkanObjects(); + void ConfigureRenderPipeline(); void ReloadSampler(); void ReloadShader(); void PrepareRendertarget(); - void RenderToMailbox(const Layout::FramebufferLayout& layout, - std::unique_ptr& mailbox, bool flipped); void ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::Regs::FramebufferConfig& framebuffer); + void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped); void DrawSingleScreenRotated(const ScreenInfo& screen_info, float x, float y, float w, float h); void DrawSingleScreen(const ScreenInfo& screen_info, float x, float y, float w, float h); void DrawSingleScreenStereoRotated(const ScreenInfo& screen_info_l, - const ScreenInfo& screen_info_r, float x, float y, float w, - float h); + const ScreenInfo& screen_info_r, float x, float y, float w, float h); void DrawSingleScreenStereo(const ScreenInfo& screen_info_l, const ScreenInfo& screen_info_r, float x, float y, float w, float h); + void UpdateFramerate(); // Loads framebuffer from emulated memory into the display information structure @@ -78,12 +80,18 @@ private: // Fills active OpenGL texture with the given RGB color. void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const ScreenInfo& screen); +private: + // Vulkan state + DrawInfo draw_info{}; VulkanState state; - // OpenGL object IDs + // Vulkan objects + vk::UniqueShaderModule vertex_shader, fragment_shader; + vk::UniqueDescriptorSet descriptor_set; + vk::UniqueDescriptorSetLayout descriptor_layout; + vk::UniquePipelineLayout pipeline_layout; + vk::UniquePipeline pipeline; VKBuffer vertex_buffer; - //OGLProgram shader; - //OGLSampler filter_sampler; /// Display information for top and bottom screens respectively std::array screen_infos; diff --git a/src/video_core/renderer_vulkan/vk_buffer.cpp b/src/video_core/renderer_vulkan/vk_buffer.cpp index 3c83c15ec..0e07c4698 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer.cpp @@ -12,19 +12,7 @@ namespace Vulkan { VKBuffer::~VKBuffer() { - if (buffer) { - if (memory != nullptr) { - g_vk_instace->GetDevice().unmapMemory(buffer_memory); - } - - auto deleter = [this]() { - auto& device = g_vk_instace->GetDevice(); - device.destroyBuffer(buffer); - device.freeMemory(buffer_memory); - }; - - g_vk_task_scheduler->Schedule(deleter); - } + Destroy(); } void VKBuffer::Create(const VKBuffer::Info& info) { @@ -46,6 +34,32 @@ void VKBuffer::Create(const VKBuffer::Info& info) { if (info.properties & vk::MemoryPropertyFlagBits::eHostVisible) { memory = device.mapMemory(buffer_memory, 0, info.size); } + + for (auto& format : info.view_formats) { + if (format != vk::Format::eUndefined) { + views[view_count++] = device.createBufferView({{}, buffer, format, 0, info.size}); + } + } +} + +void VKBuffer::Destroy() { + if (buffer) { + if (memory != nullptr) { + g_vk_instace->GetDevice().unmapMemory(buffer_memory); + } + + auto deleter = [this]() { + auto& device = g_vk_instace->GetDevice(); + device.destroyBuffer(buffer); + device.freeMemory(buffer_memory); + + for (int i = 0; i < view_count; i++) { + device.destroyBufferView(views[i]); + } + }; + + g_vk_task_scheduler->Schedule(deleter); + } } void VKBuffer::CopyBuffer(VKBuffer* src_buffer, VKBuffer* dst_buffer, vk::BufferCopy region) { diff --git a/src/video_core/renderer_vulkan/vk_buffer.h b/src/video_core/renderer_vulkan/vk_buffer.h index 45d85694d..0f3ccade4 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.h +++ b/src/video_core/renderer_vulkan/vk_buffer.h @@ -7,11 +7,14 @@ #include #include #include +#include #include #include "common/common_types.h" namespace Vulkan { +constexpr u32 MAX_BUFFER_VIEWS = 5; + /// Generic Vulkan buffer object used by almost every resource class VKBuffer final : public NonCopyable { public: @@ -19,22 +22,27 @@ public: u32 size; vk::MemoryPropertyFlags properties; vk::BufferUsageFlags usage; + std::array view_formats{}; }; VKBuffer() = default; - VKBuffer(VKBuffer&&) = default; ~VKBuffer(); + VKBuffer(VKBuffer&&) = default; + VKBuffer& operator=(VKBuffer&&) = default; + /// Create a new Vulkan buffer object void Create(const Info& info); + void Destroy(); /// Global utility functions used by other objects static u32 FindMemoryType(u32 type_filter, vk::MemoryPropertyFlags properties); static void CopyBuffer(VKBuffer* src_buffer, VKBuffer* dst_buffer, vk::BufferCopy region); /// Return a pointer to the mapped memory if the buffer is host mapped - u8* GetHostPointer() { return reinterpret_cast(memory); } - vk::Buffer& GetBuffer() { return buffer; } + u8* GetHostPointer() const { return reinterpret_cast(memory); } + const vk::BufferView& GetView(u32 i = 0) const { return views[i]; } + vk::Buffer GetBuffer() const { return buffer; } u32 GetSize() const { return buffer_info.size; } private: @@ -42,6 +50,8 @@ private: void* memory = nullptr; vk::Buffer buffer; vk::DeviceMemory buffer_memory; + u32 view_count = 0; + std::array views; }; } diff --git a/src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp b/src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp index 83588715e..68dc27a24 100644 --- a/src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp +++ b/src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp @@ -1,4 +1,4 @@ -// Copyright 2020 Citra Emulator Project +// Copyright 2022 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -12,386 +12,12 @@ namespace Vulkan { using PixelFormat = SurfaceParams::PixelFormat; -class RGBA4toRGB5A1 final : public FormatReinterpreterBase { -public: - RGBA4toRGB5A1() { - constexpr std::string_view vs_source = R"( -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(location = 0) out vec2 dst_coord; - -uniform mediump ivec2 dst_size; - -const vec2 vertices[4] = - vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); - -void main() { - gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); - dst_coord = (vertices[gl_VertexID] / 2.0 + 0.5) * vec2(dst_size); -} -)"; - - constexpr std::string_view fs_source = R"( -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout (location = 0) in mediump vec2 dst_coord; -layout (location = 0) out lowp vec4 frag_color; - -uniform lowp sampler2D source; -uniform mediump ivec2 dst_size; -uniform mediump ivec2 src_size; -uniform mediump ivec2 src_offset; - -void main() { - mediump ivec2 tex_coord; - if (src_size == dst_size) { - tex_coord = ivec2(dst_coord); - } else { - highp int tex_index = int(dst_coord.y) * dst_size.x + int(dst_coord.x); - mediump int y = tex_index / src_size.x; - tex_coord = ivec2(tex_index - y * src_size.x, y); - } - tex_coord -= src_offset; - - lowp ivec4 rgba4 = ivec4(texelFetch(source, tex_coord, 0) * (exp2(4.0) - 1.0)); - lowp ivec3 rgb5 = - ((rgba4.rgb << ivec3(1, 2, 3)) | (rgba4.gba >> ivec3(3, 2, 1))) & 0x1F; - frag_color = vec4(vec3(rgb5) / (exp2(5.0) - 1.0), rgba4.a & 0x01); -} -)"; - - program.Create(vs_source.data(), fs_source.data()); - dst_size_loc = glGetUniformLocation(program.handle, "dst_size"); - src_size_loc = glGetUniformLocation(program.handle, "src_size"); - src_offset_loc = glGetUniformLocation(program.handle, "src_offset"); - vao.Create(); - } - - void Reinterpret(Surface src_surface, const Common::Rectangle& src_rect, - Surface dst_surface, const Common::Rectangle& dst_rect) override { - OpenGLState prev_state = OpenGLState::GetCurState(); - SCOPE_EXIT({ prev_state.Apply(); }); - - OpenGLState state; - state.texture_units[0].texture_2d = src_tex; - state.draw.draw_framebuffer = draw_fb_handle; - state.draw.shader_program = program.handle; - state.draw.vertex_array = vao.handle; - state.viewport = {static_cast(dst_rect.left), static_cast(dst_rect.bottom), - static_cast(dst_rect.GetWidth()), - static_cast(dst_rect.GetHeight())}; - state.Apply(); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, - 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - - glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight()); - glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight()); - glUniform2i(src_offset_loc, src_rect.left, src_rect.bottom); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - } - -private: - OGLProgram program; - GLint dst_size_loc{-1}, src_size_loc{-1}, src_offset_loc{-1}; - OGLVertexArray vao; -}; - -class PixelBufferD24S8toABGR final : public FormatReinterpreterBase { -public: - PixelBufferD24S8toABGR() { - attributeless_vao.Create(); - d24s8_abgr_buffer.Create(); - d24s8_abgr_buffer_size = 0; - - constexpr std::string_view vs_source = R"( -const vec2 vertices[4] = vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), - vec2(-1.0, 1.0), vec2(1.0, 1.0)); -void main() { - gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); -} -)"; - - std::string fs_source = GLES ? fragment_shader_precision_OES : ""; - fs_source += R"( -uniform samplerBuffer tbo; -uniform vec2 tbo_size; -uniform vec4 viewport; - -out vec4 color; - -void main() { - vec2 tbo_coord = (gl_FragCoord.xy - viewport.xy) * tbo_size / viewport.zw; - int tbo_offset = int(tbo_coord.y) * int(tbo_size.x) + int(tbo_coord.x); - color = texelFetch(tbo, tbo_offset).rabg; -} -)"; - d24s8_abgr_shader.Create(vs_source.data(), fs_source.c_str()); - - OpenGLState state = OpenGLState::GetCurState(); - GLuint old_program = state.draw.shader_program; - state.draw.shader_program = d24s8_abgr_shader.handle; - state.Apply(); - - GLint tbo_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo"); - ASSERT(tbo_u_id != -1); - glUniform1i(tbo_u_id, 0); - - state.draw.shader_program = old_program; - state.Apply(); - - d24s8_abgr_tbo_size_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "tbo_size"); - ASSERT(d24s8_abgr_tbo_size_u_id != -1); - d24s8_abgr_viewport_u_id = glGetUniformLocation(d24s8_abgr_shader.handle, "viewport"); - ASSERT(d24s8_abgr_viewport_u_id != -1); - } - - ~PixelBufferD24S8toABGR() {} - - void Reinterpret(GLuint src_tex, const Common::Rectangle& src_rect, GLuint read_fb_handle, - GLuint dst_tex, const Common::Rectangle& dst_rect, - GLuint draw_fb_handle) override { - OpenGLState prev_state = OpenGLState::GetCurState(); - SCOPE_EXIT({ prev_state.Apply(); }); - - OpenGLState state; - state.draw.read_framebuffer = read_fb_handle; - state.draw.draw_framebuffer = draw_fb_handle; - state.Apply(); - - glBindBuffer(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer.handle); - - GLsizeiptr target_pbo_size = - static_cast(src_rect.GetWidth()) * src_rect.GetHeight() * 4; - if (target_pbo_size > d24s8_abgr_buffer_size) { - d24s8_abgr_buffer_size = target_pbo_size * 2; - glBufferData(GL_PIXEL_PACK_BUFFER, d24s8_abgr_buffer_size, nullptr, GL_STREAM_COPY); - } - - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - src_tex, 0); - glReadPixels(static_cast(src_rect.left), static_cast(src_rect.bottom), - static_cast(src_rect.GetWidth()), - static_cast(src_rect.GetHeight()), GL_DEPTH_STENCIL, - GL_UNSIGNED_INT_24_8, 0); - - glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - - // PBO now contains src_tex in RABG format - state.draw.shader_program = d24s8_abgr_shader.handle; - state.draw.vertex_array = attributeless_vao.handle; - state.viewport.x = static_cast(dst_rect.left); - state.viewport.y = static_cast(dst_rect.bottom); - state.viewport.width = static_cast(dst_rect.GetWidth()); - state.viewport.height = static_cast(dst_rect.GetHeight()); - state.Apply(); - - OGLTexture tbo; - tbo.Create(); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_BUFFER, tbo.handle); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA8, d24s8_abgr_buffer.handle); - - glUniform2f(d24s8_abgr_tbo_size_u_id, static_cast(src_rect.GetWidth()), - static_cast(src_rect.GetHeight())); - glUniform4f(d24s8_abgr_viewport_u_id, static_cast(state.viewport.x), - static_cast(state.viewport.y), - static_cast(state.viewport.width), - static_cast(state.viewport.height)); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, - 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glBindTexture(GL_TEXTURE_BUFFER, 0); - } - -private: - OGLVertexArray attributeless_vao; - OGLBuffer d24s8_abgr_buffer; - GLsizeiptr d24s8_abgr_buffer_size; - OGLProgram d24s8_abgr_shader; - GLint d24s8_abgr_tbo_size_u_id; - GLint d24s8_abgr_viewport_u_id; -}; - -class ShaderD24S8toRGBA8 final : public FormatReinterpreterBase { -public: - ShaderD24S8toRGBA8() { - constexpr std::string_view vs_source = R"( -out vec2 dst_coord; - -uniform mediump ivec2 dst_size; - -const vec2 vertices[4] = - vec2[4](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0)); - -void main() { - gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); - dst_coord = (vertices[gl_VertexID] / 2.0 + 0.5) * vec2(dst_size); -} -)"; - - constexpr std::string_view fs_source = R"( -in mediump vec2 dst_coord; - -out lowp vec4 frag_color; - -uniform highp sampler2D depth; -uniform lowp usampler2D stencil; -uniform mediump ivec2 dst_size; -uniform mediump ivec2 src_size; -uniform mediump ivec2 src_offset; - -void main() { - mediump ivec2 tex_coord; - if (src_size == dst_size) { - tex_coord = ivec2(dst_coord); - } else { - highp int tex_index = int(dst_coord.y) * dst_size.x + int(dst_coord.x); - mediump int y = tex_index / src_size.x; - tex_coord = ivec2(tex_index - y * src_size.x, y); - } - tex_coord -= src_offset; - - highp uint depth_val = - uint(texelFetch(depth, tex_coord, 0).x * (exp2(32.0) - 1.0)); - lowp uint stencil_val = texelFetch(stencil, tex_coord, 0).x; - highp uvec4 components = - uvec4(stencil_val, (uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu); - frag_color = vec4(components) / (exp2(8.0) - 1.0); -} -)"; - - program.Create(vs_source.data(), fs_source.data()); - dst_size_loc = glGetUniformLocation(program.handle, "dst_size"); - src_size_loc = glGetUniformLocation(program.handle, "src_size"); - src_offset_loc = glGetUniformLocation(program.handle, "src_offset"); - vao.Create(); - - auto state = OpenGLState::GetCurState(); - auto cur_program = state.draw.shader_program; - state.draw.shader_program = program.handle; - state.Apply(); - glUniform1i(glGetUniformLocation(program.handle, "stencil"), 1); - state.draw.shader_program = cur_program; - state.Apply(); - - // Nvidia seem to be the only one to support D24S8 views, at least on windows - // so for everyone else it will do an intermediate copy before running through the shader - std::string_view vendor{reinterpret_cast(glGetString(GL_VENDOR))}; - if (vendor.find("NVIDIA") != vendor.npos) { - use_texture_view = true; - } else { - LOG_INFO(Render_OpenGL, - "Texture views are unsupported, reinterpretation will do intermediate copy"); - temp_tex.Create(); - } - } - - void Reinterpret(GLuint src_tex, const Common::Rectangle& src_rect, GLuint read_fb_handle, - GLuint dst_tex, const Common::Rectangle& dst_rect, - GLuint draw_fb_handle) override { - OpenGLState prev_state = OpenGLState::GetCurState(); - SCOPE_EXIT({ prev_state.Apply(); }); - - OpenGLState state; - state.texture_units[0].texture_2d = src_tex; - - if (use_texture_view) { - temp_tex.Create(); - glActiveTexture(GL_TEXTURE1); - glTextureView(temp_tex.handle, GL_TEXTURE_2D, src_tex, GL_DEPTH24_STENCIL8, 0, 1, 0, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else if (src_rect.top > temp_rect.top || src_rect.right > temp_rect.right) { - temp_tex.Release(); - temp_tex.Create(); - state.texture_units[1].texture_2d = temp_tex.handle; - state.Apply(); - glActiveTexture(GL_TEXTURE1); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, src_rect.right, src_rect.top); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - temp_rect = src_rect; - } - - state.texture_units[1].texture_2d = temp_tex.handle; - state.draw.draw_framebuffer = draw_fb_handle; - state.draw.shader_program = program.handle; - state.draw.vertex_array = vao.handle; - state.viewport = {static_cast(dst_rect.left), static_cast(dst_rect.bottom), - static_cast(dst_rect.GetWidth()), - static_cast(dst_rect.GetHeight())}; - state.Apply(); - - glActiveTexture(GL_TEXTURE1); - if (!use_texture_view) { - glCopyImageSubData(src_tex, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, - temp_tex.handle, GL_TEXTURE_2D, 0, src_rect.left, src_rect.bottom, 0, - src_rect.GetWidth(), src_rect.GetHeight(), 1); - } - glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, - 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - - glUniform2i(dst_size_loc, dst_rect.GetWidth(), dst_rect.GetHeight()); - glUniform2i(src_size_loc, src_rect.GetWidth(), src_rect.GetHeight()); - glUniform2i(src_offset_loc, src_rect.left, src_rect.bottom); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - if (use_texture_view) { - temp_tex.Release(); - } - } - -private: - bool use_texture_view{}; - OGLProgram program{}; - GLint dst_size_loc{-1}, src_size_loc{-1}, src_offset_loc{-1}; - OGLVertexArray vao{}; - OGLTexture temp_tex{}; - Common::Rectangle temp_rect{0, 0, 0, 0}; -}; - -FormatReinterpreterOpenGL::FormatReinterpreterOpenGL() { - const std::string_view vendor{reinterpret_cast(glGetString(GL_VENDOR))}; - const std::string_view version{reinterpret_cast(glGetString(GL_VERSION))}; - // Fallback to PBO path on obsolete intel drivers - // intel`s GL_VERSION string - `3.3.0 - Build 25.20.100.6373` - const bool intel_broken_drivers = - vendor.find("Intel") != vendor.npos && (std::atoi(version.substr(14, 2).data()) < 30); - - if ((!intel_broken_drivers && GLAD_GL_ARB_stencil_texturing && GLAD_GL_ARB_texture_storage && - GLAD_GL_ARB_copy_image) || - GLES) { - reinterpreters.emplace(PixelFormatPair{PixelFormat::RGBA8, PixelFormat::D24S8}, - std::make_unique()); - LOG_INFO(Render_OpenGL, "Using shader for D24S8 to RGBA8 reinterpretation"); - } else { - reinterpreters.emplace(PixelFormatPair{PixelFormat::RGBA8, PixelFormat::D24S8}, - std::make_unique()); - LOG_INFO(Render_OpenGL, "Using pbo for D24S8 to RGBA8 reinterpretation"); - } - reinterpreters.emplace(PixelFormatPair{PixelFormat::RGB5A1, PixelFormat::RGBA4}, - std::make_unique()); +FormatReinterpreterVulkan::FormatReinterpreterVulkan() { } -FormatReinterpreterOpenGL::~FormatReinterpreterOpenGL() = default; - -std::pair -FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format) { +std::pair +FormatReinterpreterVulkan::GetPossibleReinterpretations(PixelFormat dst_format) { return reinterpreters.equal_range(dst_format); } diff --git a/src/video_core/renderer_vulkan/vk_format_reinterpreter.h b/src/video_core/renderer_vulkan/vk_format_reinterpreter.h index f41f55015..efdae586c 100644 --- a/src/video_core/renderer_vulkan/vk_format_reinterpreter.h +++ b/src/video_core/renderer_vulkan/vk_format_reinterpreter.h @@ -51,7 +51,7 @@ class FormatReinterpreterVulkan : NonCopyable { public: explicit FormatReinterpreterVulkan(); - ~FormatReinterpreterVulkan(); + ~FormatReinterpreterVulkan() = default; std::pair GetPossibleReinterpretations( SurfaceParams::PixelFormat dst_format); diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 9e7812099..c409cc9c3 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -13,8 +13,7 @@ namespace Vulkan { /// The global Vulkan instance -class VKInstance -{ +class VKInstance { public: VKInstance() = default; ~VKInstance(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_builder.cpp b/src/video_core/renderer_vulkan/vk_pipeline_builder.cpp index 48e7baff1..74d3da163 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_builder.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_builder.cpp @@ -11,27 +11,85 @@ namespace Vulkan { -PipelineBuilder::PipelineBuilder() { - vertex_input_state = vk::PipelineVertexInputStateCreateInfo{ - {}, HardwareVertex::binding_desc, HardwareVertex::attribute_desc - }; +PipelineLayoutBuilder::PipelineLayoutBuilder() { + Clear(); +} - /* Include all required pointers to the pipeline info structure */ - vk::GraphicsPipelineCreateInfo pipeline_info{ - {}, 0, shader_stages.data(), &vertex_input_state, &input_assembly, nullptr, - &viewport_state, &rasterization_state, &multisample_info, &depth_state, - &blend_state, &dynamic_info, nullptr, nullptr }; +void PipelineLayoutBuilder::Clear() { + pipeline_layout_info = vk::PipelineLayoutCreateInfo{}; +} + +vk::PipelineLayout PipelineLayoutBuilder::Build() { + auto& device = g_vk_instace->GetDevice(); + + auto result = device.createPipelineLayout(pipeline_layout_info); + if (result) { + LOG_ERROR(Render_Vulkan, "Failed to create pipeline layout"); + return VK_NULL_HANDLE; + } + + Clear(); + return result; +} + +void PipelineLayoutBuilder::AddDescriptorSet(vk::DescriptorSetLayout layout) { + assert(pipeline_layout_info.setLayoutCount < MAX_SETS); + + sets[pipeline_layout_info.setLayoutCount++] = layout; + pipeline_layout_info.pSetLayouts = sets.data(); +} + +void PipelineLayoutBuilder::AddPushConstants(vk::ShaderStageFlags stages, u32 offset, u32 size) { + assert(pipeline_layout_info.pushConstantRangeCount < MAX_PUSH_CONSTANTS); + + push_constants[pipeline_layout_info.pushConstantRangeCount++] = {stages, offset, size}; + pipeline_layout_info.pPushConstantRanges = push_constants.data(); +} + +PipelineBuilder::PipelineBuilder() { + Clear(); +} + +void PipelineBuilder::Clear() { + pipeline_info = vk::GraphicsPipelineCreateInfo{}; + shader_stages.clear(); + + vertex_input_state = vk::PipelineVertexInputStateCreateInfo{}; + input_assembly = vk::PipelineInputAssemblyStateCreateInfo{}; + rasterization_state = vk::PipelineRasterizationStateCreateInfo{}; + depth_state = vk::PipelineDepthStencilStateCreateInfo{}; + + blend_state = vk::PipelineColorBlendStateCreateInfo{}; + blend_attachment = vk::PipelineColorBlendAttachmentState{}; + dynamic_info = vk::PipelineDynamicStateCreateInfo{}; + dynamic_states.fill({}); + + viewport_state = vk::PipelineViewportStateCreateInfo{}; + multisample_info = vk::PipelineMultisampleStateCreateInfo{}; + + // Set defaults + SetNoCullRasterizationState(); + SetNoDepthTestState(); + SetNoBlendingState(); + SetPrimitiveTopology(vk::PrimitiveTopology::eTriangleList); + + // Have to be specified even if dynamic + SetViewport(0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f); + SetScissorRect(0, 0, 1, 1); + SetBlendConstants(1.0f, 1.0f, 1.0f, 1.0f); + SetMultisamples(vk::SampleCountFlagBits::e1, false); } vk::Pipeline PipelineBuilder::Build() { auto& device = g_vk_instace->GetDevice(); - auto result = device.createGraphicsPipeline({}, pipeline_info); + auto result = device.createGraphicsPipeline({}, pipeline_info); if (result.result != vk::Result::eSuccess) { LOG_CRITICAL(Render_Vulkan, "Failed to build vulkan pipeline!"); UNREACHABLE(); } + Clear(); return result.value; } @@ -54,6 +112,23 @@ void PipelineBuilder::SetShaderStage(vk::ShaderStageFlagBits stage, vk::ShaderMo } } +void PipelineBuilder::AddVertexBuffer(u32 binding, u32 stride, vk::VertexInputRate input_rate, + std::span attributes) { + assert(vertex_input_state.vertexAttributeDescriptionCount + attributes.size() < MAX_VERTEX_BUFFERS); + + // Copy attributes to private array + auto loc = vertex_attributes.begin() + vertex_input_state.vertexAttributeDescriptionCount; + std::copy(attributes.begin(), attributes.end(), loc); + + vertex_buffers[vertex_input_state.vertexBindingDescriptionCount++] = {binding, stride, input_rate}; + vertex_input_state.vertexAttributeDescriptionCount += attributes.size(); + + vertex_input_state.pVertexBindingDescriptions = vertex_buffers.data(); + vertex_input_state.pVertexAttributeDescriptions = vertex_attributes.data(); + + pipeline_info.pVertexInputState = &vertex_input_state; +} + void PipelineBuilder::SetPrimitiveTopology(vk::PrimitiveTopology topology, bool enable_primitive_restart) { input_assembly.topology = topology; input_assembly.primitiveRestartEnable = enable_primitive_restart; @@ -65,32 +140,42 @@ void PipelineBuilder::SetRasterizationState(vk::PolygonMode polygon_mode, vk::Cu rasterization_state.polygonMode = polygon_mode; rasterization_state.cullMode = cull_mode; rasterization_state.frontFace = front_face; + pipeline_info.pRasterizationState = &rasterization_state; } void PipelineBuilder::SetLineWidth(float width) { rasterization_state.lineWidth = width; + pipeline_info.pRasterizationState = &rasterization_state; } -void PipelineBuilder::SetMultisamples(u32 multisamples, bool per_sample_shading) { - multisample_info.rasterizationSamples = static_cast(multisamples); +void PipelineBuilder::SetMultisamples(vk::SampleCountFlagBits samples, bool per_sample_shading) { + multisample_info.rasterizationSamples = samples; multisample_info.sampleShadingEnable = per_sample_shading; - multisample_info.minSampleShading = (multisamples > 1) ? 1.0f : 0.0f; + multisample_info.minSampleShading = (static_cast(samples) > 1) ? 1.0f : 0.0f; + pipeline_info.pMultisampleState = &multisample_info; +} + +void PipelineBuilder::SetNoCullRasterizationState() { + SetRasterizationState(vk::PolygonMode::eFill, vk::CullModeFlagBits::eNone, vk::FrontFace::eClockwise); } void PipelineBuilder::SetDepthState(bool depth_test, bool depth_write, vk::CompareOp compare_op) { depth_state.depthTestEnable = depth_test; depth_state.depthWriteEnable = depth_write; depth_state.depthCompareOp = compare_op; + pipeline_info.pDepthStencilState = &depth_state; } void PipelineBuilder::SetStencilState(bool stencil_test, vk::StencilOpState front, vk::StencilOpState back) { depth_state.stencilTestEnable = stencil_test; depth_state.front = front; depth_state.back = back; + pipeline_info.pDepthStencilState = &depth_state; } void PipelineBuilder::SetBlendConstants(float r, float g, float b, float a) { blend_state.blendConstants = std::array{r, g, b, a}; + pipeline_info.pColorBlendState = &blend_state; } void PipelineBuilder::SetBlendAttachment(bool blend_enable, vk::BlendFactor src_factor, vk::BlendFactor dst_factor, @@ -108,6 +193,7 @@ void PipelineBuilder::SetBlendAttachment(bool blend_enable, vk::BlendFactor src_ blend_state.attachmentCount = 1; blend_state.pAttachments = &blend_attachment; + pipeline_info.pColorBlendState = &blend_state; } void PipelineBuilder::SetNoBlendingState() { @@ -116,29 +202,33 @@ void PipelineBuilder::SetNoBlendingState() { vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA); } -void PipelineBuilder::AddDynamicState(vk::DynamicState state) { - if (dynamic_info.dynamicStateCount < MAX_DYNAMIC_STATES) { - dynamic_states[dynamic_info.dynamicStateCount] = state; - - dynamic_info.dynamicStateCount++; - dynamic_info.pDynamicStates = dynamic_states.data(); - return; +void PipelineBuilder::SetDynamicStates(std::span states) { + if (states.size() > MAX_DYNAMIC_STATES) { + LOG_ERROR(Render_Vulkan, "Cannot include more dynamic states!"); + UNREACHABLE(); } - LOG_ERROR(Render_Vulkan, "Cannot include more dynamic states!"); - UNREACHABLE(); + // Copy the state data + std::copy(states.begin(), states.end(), dynamic_states.begin()); + dynamic_info.dynamicStateCount = states.size(); + + dynamic_info.pDynamicStates = dynamic_states.data(); + pipeline_info.pDynamicState = &dynamic_info; + return; } void PipelineBuilder::SetViewport(float x, float y, float width, float height, float min_depth, float max_depth) { viewport = vk::Viewport{ x, y, width, height, min_depth, max_depth }; viewport_state.pViewports = &viewport; viewport_state.viewportCount = 1; + pipeline_info.pViewportState = &viewport_state; } void PipelineBuilder::SetScissorRect(s32 x, s32 y, u32 width, u32 height) { scissor = vk::Rect2D{{x, y}, {width, height}}; - viewport_state.pScissors = &scissor; viewport_state.scissorCount = 1u; + viewport_state.pScissors = &scissor; + pipeline_info.pViewportState = &viewport_state; } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_builder.h b/src/video_core/renderer_vulkan/vk_pipeline_builder.h index 5fb800950..722cdd0b3 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_builder.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_builder.h @@ -15,25 +15,46 @@ namespace Vulkan { -constexpr u32 MAX_DYNAMIC_STATES = 14; -constexpr u32 MAX_SHADER_STAGES = 3; +class PipelineLayoutBuilder { +public: + PipelineLayoutBuilder(); + ~PipelineLayoutBuilder() = default; + + void Clear(); + vk::PipelineLayout Build(); + + void AddDescriptorSet(vk::DescriptorSetLayout layout); + void AddPushConstants(vk::ShaderStageFlags stages, u32 offset, u32 size); + +private: + static constexpr u32 MAX_SETS = 8; + static constexpr u32 MAX_PUSH_CONSTANTS = 1; + + vk::PipelineLayoutCreateInfo pipeline_layout_info; + std::array sets; + std::array push_constants; +}; class PipelineBuilder { public: PipelineBuilder(); ~PipelineBuilder() = default; + void Clear(); vk::Pipeline Build(); void SetPipelineLayout(vk::PipelineLayout layout); + void AddVertexBuffer(u32 binding, u32 stride, vk::VertexInputRate input_rate, + const std::span attributes); void SetShaderStage(vk::ShaderStageFlagBits stage, vk::ShaderModule module); void SetPrimitiveTopology(vk::PrimitiveTopology topology, bool enable_primitive_restart = false); void SetLineWidth(float width); - void SetMultisamples(u32 multisamples, bool per_sample_shading); + void SetMultisamples(vk::SampleCountFlagBits samples, bool per_sample_shading); void SetRasterizationState(vk::PolygonMode polygon_mode, vk::CullModeFlags cull_mode, vk::FrontFace front_face); + void SetNoCullRasterizationState(); void SetDepthState(bool depth_test, bool depth_write, vk::CompareOp compare_op); void SetStencilState(bool stencil_test, vk::StencilOpState front, vk::StencilOpState back); void SetNoDepthTestState(); @@ -47,14 +68,21 @@ public: void SetViewport(float x, float y, float width, float height, float min_depth, float max_depth); void SetScissorRect(s32 x, s32 y, u32 width, u32 height); - void AddDynamicState(vk::DynamicState state); - void SetMultisamples(vk::SampleCountFlagBits samples); + void SetDynamicStates(std::span states); private: + static constexpr u32 MAX_DYNAMIC_STATES = 14; + static constexpr u32 MAX_SHADER_STAGES = 3; + static constexpr u32 MAX_VERTEX_BUFFERS = 8; + static constexpr u32 MAX_VERTEX_ATTRIBUTES = 16; + vk::GraphicsPipelineCreateInfo pipeline_info; std::vector shader_stages; vk::PipelineVertexInputStateCreateInfo vertex_input_state; + std::array vertex_buffers; + std::array vertex_attributes; + vk::PipelineInputAssemblyStateCreateInfo input_assembly; vk::PipelineRasterizationStateCreateInfo rasterization_state; vk::PipelineDepthStencilStateCreateInfo depth_state; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 9eb41a72b..d50957969 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -76,7 +76,10 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window) { .usage = vk::BufferUsageFlagBits::eStorageTexelBuffer, }; + texel_buffer_info.view_formats[0] = vk::Format::eR32G32Sfloat; texture_buffer_lut_lf.Create(texel_buffer_info); + + texel_buffer_info.view_formats[1] = vk::Format::eR32G32B32A32Sfloat; texture_buffer_lut.Create(texel_buffer_info); // Create and bind uniform buffers @@ -87,13 +90,15 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window) { }; uniform_buffer.Create(uniform_info); - state.SetUniformBuffer(BindingID::VertexUniform, &uniform_buffer, 0, uniform_size_aligned_vs); - state.SetUniformBuffer(BindingID::PicaUniform, &uniform_buffer, uniform_size_aligned_vs, uniform_size_aligned_fs); + state.SetUniformBuffer(BindingID::VertexUniform, 0, uniform_size_aligned_vs, + uniform_buffer); + state.SetUniformBuffer(BindingID::PicaUniform, uniform_size_aligned_vs, uniform_size_aligned_fs, + uniform_buffer); // Bind texel buffers - state.SetTexelBuffer(BindingID::LutLF, &texture_buffer_lut_lf, vk::Format::eR32G32Sfloat); - state.SetTexelBuffer(BindingID::LutRG, &texture_buffer_lut, vk::Format::eR32G32Sfloat); - state.SetTexelBuffer(BindingID::LutRGBA, &texture_buffer_lut, vk::Format::eR32G32B32A32Sfloat); + state.SetTexelBuffer(BindingID::LutLF, 0, TEXTURE_BUFFER_SIZE, texture_buffer_lut_lf, 0); + state.SetTexelBuffer(BindingID::LutRG, 0, TEXTURE_BUFFER_SIZE, texture_buffer_lut, 0); + state.SetTexelBuffer(BindingID::LutRGBA, 0, TEXTURE_BUFFER_SIZE, texture_buffer_lut, 1); // Create vertex and index buffers VKBuffer::Info vertex_info = { @@ -331,7 +336,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { //texture_samplers[texture_index].SyncWithConfig(texture.config); Surface surface = res_cache.GetTextureSurface(texture); if (surface != nullptr) { - state.SetTexture(BindingID::Tex0 + texture_index, &surface->texture); + state.SetTexture(BindingID::Tex0 + texture_index, surface->texture); } else { // Can occur when texture addr is null or its memory is unmapped/invalid // HACK: In this case, the correct behaviour for the PICA is to use the last @@ -405,13 +410,6 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { vertex_batch.clear(); - // Reset textures in rasterizer state context because the rasterizer cache might delete them - for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { - state.UnbindTexture(texture_index); - } - - state.Apply(); - // Mark framebuffer surfaces as dirty Common::Rectangle draw_rect_unscaled{draw_rect.left / res_scale, draw_rect.top / res_scale, draw_rect.right / res_scale, @@ -1132,7 +1130,7 @@ bool RasterizerVulkan::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con (float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width, (float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width); - screen_info.display_texture = &src_surface->texture; + screen_info.display_texture = src_surface->texture.GetHandle(); return true; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b0b82c96f..e549b89f9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -280,15 +280,12 @@ private: static constexpr std::size_t UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024; static constexpr std::size_t TEXTURE_BUFFER_SIZE = 1 * 1024 * 1024; - std::array texture_samplers; VKBuffer vertex_buffer, uniform_buffer, index_buffer; VKBuffer texture_buffer_lut_lf, texture_buffer_lut; u32 uniform_buffer_alignment; u32 uniform_size_aligned_vs, uniform_size_aligned_fs; - SamplerInfo texture_cube_sampler; - std::array, Pica::LightingRegs::NumLightingSampler> lighting_lut_data{}; std::array fog_lut_data{}; diff --git a/src/video_core/renderer_vulkan/vk_state.cpp b/src/video_core/renderer_vulkan/vk_state.cpp index 808f40fce..8b261e2a3 100644 --- a/src/video_core/renderer_vulkan/vk_state.cpp +++ b/src/video_core/renderer_vulkan/vk_state.cpp @@ -36,6 +36,51 @@ bool operator <(BindingID lhs, BindingID rhs) { static_cast(rhs); } +DescriptorUpdater::DescriptorUpdater() { + Clear(); +} + +void DescriptorUpdater::Clear() { + writes = {}; + write_count = 0; +} + +void DescriptorUpdater::Update() { + assert(write_count > 0); + + auto& device = g_vk_instace->GetDevice(); + device.updateDescriptorSets(writes, {}); +} + +void DescriptorUpdater::SetDescriptorSet(vk::DescriptorSet _set) { + set = _set; +} + +void DescriptorUpdater::AddCombinedImageSamplerDescriptorWrite(u32 binding, vk::Sampler sampler, + const VKTexture& image) { + assert(write_count < MAX_WRITES && image_info_count < MAX_IMAGE_INFOS); + + auto& info = image_infos[image_info_count++]; + info = vk::DescriptorImageInfo{sampler, image.GetView(), image.GetLayout()}; + + writes[write_count++] = vk::WriteDescriptorSet{ + set, binding, 0, 1, vk::DescriptorType::eCombinedImageSampler, &info + }; +} + +void DescriptorUpdater::AddBufferDescriptorWrite(u32 binding, vk::DescriptorType buffer_type, + u32 offset, u32 size, const VKBuffer& buffer, + const vk::BufferView& view) { + assert(write_count < MAX_WRITES && buffer_info_count < MAX_BUFFER_INFOS); + + auto& info = buffer_infos[buffer_info_count++]; + info = vk::DescriptorBufferInfo{buffer.GetBuffer(), offset, size}; + + writes[write_count++] = vk::WriteDescriptorSet{ + set, binding, 0, 1, buffer_type, nullptr, &info, &view + }; +} + VulkanState::VulkanState() { // Create a dummy texture which can be used in place of a real binding. VKTexture::Info info = { @@ -46,20 +91,8 @@ VulkanState::VulkanState() { .view_type = vk::ImageViewType::e2D }; - dummy_texture.Create(info); - dummy_texture.Transition(vk::ImageLayout::eShaderReadOnlyOptimal); - - // Create descriptor pool - // TODO: Choose sizes more wisely - const std::array 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); + placeholder.Create(info); + placeholder.Transition(vk::ImageLayout::eShaderReadOnlyOptimal); // Create texture sampler auto props = g_vk_instace->GetPhysicalDevice().getProperties(); @@ -71,6 +104,8 @@ VulkanState::VulkanState() { false, vk::CompareOp::eAlways, {}, {}, vk::BorderColor::eIntOpaqueBlack, false }; + + auto& device = g_vk_instace->GetDevice(); sampler = device.createSamplerUnique(sampler_info); // Compile trivial vertex shader @@ -105,60 +140,44 @@ void VulkanState::SetVertexBuffer(VKBuffer* buffer, vk::DeviceSize offset) { dirty_flags |= DirtyFlags::VertexBuffer; } -void VulkanState::SetUniformBuffer(BindingID id, VKBuffer* buffer, u32 offset, u32 size) { +void VulkanState::SetUniformBuffer(BindingID id, u32 offset, u32 size, const VKBuffer& buffer) { assert(id < BindingID::Tex0); - u32 index = static_cast(id); + u32 binding = static_cast(id); - auto& binding = bindings[index]; - auto old_buffer = std::get(binding.resource); - if (old_buffer != buffer) { - binding.resource = buffer; - dirty_flags |= DirtyFlags::Uniform; - binding.dirty = true; - } + updater.SetDescriptorSet(g_vk_task_scheduler->GetDescriptorSet(0)); + updater.AddBufferDescriptorWrite(binding, vk::DescriptorType::eUniformBuffer, offset, size, buffer); } -void VulkanState::SetTexture(BindingID id, VKTexture* image) { +void VulkanState::SetTexture(BindingID id, const VKTexture& image) { assert(id > BindingID::PicaUniform && id < BindingID::LutLF); - u32 index = static_cast(id); + u32 binding = static_cast(id); - auto& binding = bindings[index]; - auto old_image = std::get(binding.resource); - if (old_image != image) { - binding.resource = image; - dirty_flags |= DirtyFlags::Texture; - binding.dirty = true; - } + updater.SetDescriptorSet(g_vk_task_scheduler->GetDescriptorSet(1)); + updater.AddCombinedImageSamplerDescriptorWrite(binding, sampler.get(), image); } -void VulkanState::SetTexelBuffer(BindingID id, VKBuffer* buffer, vk::Format view_format) { +void VulkanState::SetTexelBuffer(BindingID id, u32 offset, u32 size, const VKBuffer& buffer, u32 view_index) { assert(id > BindingID::TexCube); - u32 index = static_cast(id); + u32 binding = static_cast(id); - auto& binding = bindings[index]; - auto old_buffer = std::get(binding.resource); - if (old_buffer != buffer) { - auto& device = g_vk_instace->GetDevice(); - - binding.resource = buffer; - binding.buffer_view = device.createBufferViewUnique({{}, buffer->GetBuffer(), view_format}); - dirty_flags |= DirtyFlags::TexelBuffer; - binding.dirty = true; - } + updater.SetDescriptorSet(g_vk_task_scheduler->GetDescriptorSet(2)); + updater.AddBufferDescriptorWrite(binding, vk::DescriptorType::eStorageTexelBuffer, + offset, size, buffer, buffer.GetView(view_index)); } -void VulkanState::UnbindTexture(VKTexture* image) { +void VulkanState::UnbindTexture(const VKTexture& image) { + updater.SetDescriptorSet(g_vk_task_scheduler->GetDescriptorSet(1)); for (auto i = u32(BindingID::Tex0); i <= u32(BindingID::TexCube); i++) { - auto current_image = std::get(bindings[i].resource); - if (current_image == image) { - UnbindTexture(i); + auto view = updater.GetResource(i); + if (view == image.GetView()) { + updater.AddCombinedImageSamplerDescriptorWrite(i, sampler.get(), placeholder); } } } void VulkanState::UnbindTexture(u32 index) { - bindings[index].resource = &dummy_texture; - dirty_flags |= DirtyFlags::Texture; + updater.SetDescriptorSet(g_vk_task_scheduler->GetDescriptorSet(1)); + updater.AddCombinedImageSamplerDescriptorWrite(index, sampler.get(), placeholder); } void VulkanState::BeginRendering(Attachment color, Attachment depth_stencil) { @@ -383,7 +402,7 @@ vk::ShaderModule VulkanState::CompileShader(const std::string& source, vk::Shade void VulkanState::Apply() { // Update resources in descriptor sets if changed - UpdateDescriptorSet(); + updater.Update(); // Re-apply dynamic parts of the pipeline auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); @@ -444,21 +463,21 @@ void VulkanState::Apply() { } void VulkanState::ConfigureDescriptorSets() { - // Define the descriptor sets we will be using - std::array ubo_set = {{ + // Draw descriptor sets + std::array ubo_set{{ { 0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eGeometry | vk::ShaderStageFlagBits::eFragment }, // shader_data { 1, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex } // pica_uniforms }}; - std::array texture_set = {{ + std::array texture_set{{ { 0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex0 { 1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex1 { 2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex2 { 3, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment }, // tex_cube }}; - std::array lut_set = {{ + std::array lut_set{{ { 0, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_lf { 1, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment }, // texture_buffer_lut_rg { 2, vk::DescriptorType::eStorageTexelBuffer, 1, vk::ShaderStageFlagBits::eFragment } // texture_buffer_lut_rgba @@ -476,13 +495,9 @@ void VulkanState::ConfigureDescriptorSets() { descriptor_layouts[i] = device.createDescriptorSetLayout(create_infos[i]); } - vk::DescriptorSetAllocateInfo alloc_info(desc_pool.get(), descriptor_layouts); - descriptor_sets = device.allocateDescriptorSetsUnique(alloc_info); - // Create the standard descriptor set layout vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts); pipeline_layout = device.createPipelineLayoutUnique(layout_info); - } void VulkanState::ConfigurePipeline() { @@ -505,7 +520,7 @@ void VulkanState::ConfigurePipeline() { // Enable every required dynamic state - std::array dynamic_states{ + std::array dynamic_states{ vk::DynamicState::eDepthCompareOp, vk::DynamicState::eLineWidth, vk::DynamicState::eDepthTestEnable, vk::DynamicState::eColorWriteEnableEXT, vk::DynamicState::eStencilTestEnable, vk::DynamicState::eStencilOp, @@ -515,61 +530,14 @@ void VulkanState::ConfigurePipeline() { vk::DynamicState::eLogicOpEXT, vk::DynamicState::eFrontFace }; - for (auto& state : dynamic_states) { - builder.AddDynamicState(state); - } + builder.SetDynamicStates(dynamic_states); + + // Configure vertex buffer + auto attributes = HardwareVertex::attribute_desc; + builder.AddVertexBuffer(0, sizeof(HardwareVertex), vk::VertexInputRate::eVertex, attributes); // Add trivial vertex shader builder.SetShaderStage(vk::ShaderStageFlagBits::eVertex, trivial_vertex_shader.get()); } -void VulkanState::UpdateDescriptorSet() { - std::vector writes; - std::vector buffer_infos; - std::vector image_infos; - - auto& device = g_vk_instace->GetDevice(); - - // Check if any resource has been updated - if (dirty_flags & DirtyFlags::Uniform) { - for (int i = 0; i < 2; i++) { - if (bindings[i].dirty) { - auto buffer = std::get(bindings[i].resource); - buffer_infos.emplace_back(buffer->GetBuffer(), 0, VK_WHOLE_SIZE); - writes.emplace_back(descriptor_sets[i].get(), i, 0, 1, vk::DescriptorType::eUniformBuffer, - nullptr, &buffer_infos.back(), nullptr); - bindings[i].dirty = false; - } - } - } - - if (dirty_flags & DirtyFlags::Texture) { - for (int i = 2; i < 6; i++) { - if (bindings[i].dirty) { - auto texture = std::get(bindings[i].resource); - image_infos.emplace_back(sampler.get(), texture->GetView(), vk::ImageLayout::eShaderReadOnlyOptimal); - writes.emplace_back(descriptor_sets[i].get(), i, 0, 1, vk::DescriptorType::eCombinedImageSampler, - &image_infos.back()); - bindings[i].dirty = false; - } - } - } - - if (dirty_flags & DirtyFlags::TexelBuffer) { - for (int i = 6; i < 9; i++) { - if (bindings[i].dirty) { - auto buffer = std::get(bindings[i].resource); - buffer_infos.emplace_back(buffer->GetBuffer(), 0, VK_WHOLE_SIZE); - writes.emplace_back(descriptor_sets[i].get(), i, 0, 1, vk::DescriptorType::eStorageTexelBuffer, - nullptr, &buffer_infos.back(), &bindings[i].buffer_view.get()); - bindings[i].dirty = false; - } - } - } - - if (!writes.empty()) { - device.updateDescriptorSets(writes, {}); - } -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_state.h b/src/video_core/renderer_vulkan/vk_state.h index c6dcff112..4ca4bbd84 100644 --- a/src/video_core/renderer_vulkan/vk_state.h +++ b/src/video_core/renderer_vulkan/vk_state.h @@ -64,6 +64,41 @@ struct Attachment { constexpr u32 DESCRIPTOR_SET_LAYOUT_COUNT = 3; +class DescriptorUpdater { + enum : u32 + { + MAX_WRITES = 16, + MAX_IMAGE_INFOS = 8, + MAX_BUFFER_INFOS = 4, + MAX_VIEWS = 4, + MAX_SETS = 6 + }; + +public: + DescriptorUpdater(); + ~DescriptorUpdater() = default; + + void Clear(); + void Update(); + + template + T GetResource(u32 binding); + + void SetDescriptorSet(vk::DescriptorSet set); + void AddCombinedImageSamplerDescriptorWrite(u32 binding, vk::Sampler sampler, const VKTexture& image); + void AddBufferDescriptorWrite(u32 binding, vk::DescriptorType buffer_type, u32 offset, u32 size, + const VKBuffer& buffer, const vk::BufferView& view = VK_NULL_HANDLE); +private: + vk::DescriptorSet set; + std::array writes; + std::array buffer_infos; + std::array image_infos; + std::array views; + + u32 write_count = 0, buffer_info_count = 0; + u32 image_info_count = 0, view_count = 0; +}; + /// Tracks global Vulkan state class VulkanState { public: @@ -73,6 +108,7 @@ public: /// Initialize object to its initial state static void Create(); static VulkanState& Get(); + static vk::ShaderModule CompileShader(const std::string& source, vk::ShaderStageFlagBits stage); /// Query state bool DepthTestEnabled() const { return depth_enabled && depth_writes; } @@ -88,7 +124,7 @@ public: void SetStencilWrite(u32 mask); void SetStencilInput(u32 mask); void SetStencilTest(bool enable, vk::StencilOp fail, vk::StencilOp pass, vk::StencilOp depth_fail, - vk::CompareOp compare, u32 ref); + vk::CompareOp compare, u32 ref); void SetDepthWrite(bool enable); void SetDepthTest(bool enable, vk::CompareOp compare); void SetColorMask(bool red, bool green, bool blue, bool alpha); @@ -103,10 +139,10 @@ public: void EndRendering(); /// Configure shader resources - void SetUniformBuffer(BindingID id, VKBuffer* buffer, u32 offset, u32 size); - void SetTexture(BindingID id, VKTexture* texture); - void SetTexelBuffer(BindingID id, VKBuffer* buffer, vk::Format view_format); - void UnbindTexture(VKTexture* image); + void SetUniformBuffer(BindingID id, u32 offset, u32 size, const VKBuffer& buffer); + void SetTexture(BindingID id, const VKTexture& texture); + void SetTexelBuffer(BindingID id, u32 offset, u32 size, const VKBuffer& buffer, u32 view_index); + void UnbindTexture(const VKTexture& image); void UnbindTexture(u32 index); /// Apply all dirty state to the current Vulkan command buffer @@ -115,27 +151,18 @@ public: private: void ConfigureDescriptorSets(); void ConfigurePipeline(); - void UpdateDescriptorSet(); - vk::ShaderModule CompileShader(const std::string& source, vk::ShaderStageFlagBits stage); private: - struct Binding { - bool dirty{}; - std::variant resource{}; - vk::UniqueBufferView buffer_view{}; - }; - DirtyFlags dirty_flags; - bool rendering = false; - VKTexture dummy_texture; + DescriptorUpdater updater; + VKTexture placeholder; vk::UniqueSampler sampler; + // Vertex buffer VKBuffer* vertex_buffer{}, * index_buffer{}; vk::DeviceSize vertex_offset, index_offset; - std::array bindings; - std::vector descriptor_sets; - vk::UniqueDescriptorPool desc_pool; + // Viewport vk::Viewport viewport{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; vk::CullModeFlags cull_mode{}; vk::FrontFace front_face{}; @@ -143,6 +170,7 @@ private: vk::LogicOp logic_op{}; std::array blend_constants{}; + bool rendering = false; u32 stencil_write_mask{}, stencil_input_mask{}, stencil_ref{}; bool depth_enabled{}, depth_writes{}, stencil_enabled{}, stencil_writes{}; vk::StencilOp fail_op, pass_op, depth_fail_op; @@ -159,4 +187,20 @@ private: std::unordered_map pipelines; }; +template +T DescriptorUpdater::GetResource(u32 binding) { + for (auto& write : writes) { + if (write.dstBinding == binding) { + if constexpr (std::is_same_v) { + return write.pImageInfo[0].imageView; + } + else if constexpr (std::is_same_v) { + return write.pBufferInfo[0].buffer; + } + } + } + + return VK_NULL_HANDLE; +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 0a6eea99f..16bab6c7b 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -189,17 +189,11 @@ void VKSwapChain::SetupImages() { // Create the swapchain buffers containing the image and imageview swapchain_images.resize(images.size()); - for (int i = 0; i < swapchain_images.size(); i++) - { - vk::ImageViewCreateInfo color_attachment_view - ( - {}, - images[i], - vk::ImageViewType::e2D, - details.format.format, - {}, + for (int i = 0; i < swapchain_images.size(); i++) { + vk::ImageViewCreateInfo color_attachment_view{ + {}, images[i], vk::ImageViewType::e2D, details.format.format, {}, { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 } - ); + }; // Wrap swapchain images with VKTexture swapchain_images[i].image = images[i]; diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp index af0e5f5cc..225706b9c 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp @@ -9,9 +9,6 @@ namespace Vulkan { -VKTaskScheduler::VKTaskScheduler(VKSwapChain* swapchain) : - swapchain(swapchain) {} - VKTaskScheduler::~VKTaskScheduler() { SyncToGPU(); } @@ -50,23 +47,26 @@ bool VKTaskScheduler::Create() { .usage = vk::BufferUsageFlagBits::eTransferSrc }; + const std::array pool_sizes{{ + { vk::DescriptorType::eUniformBuffer, 32 }, + { vk::DescriptorType::eCombinedImageSampler, 32 }, + { vk::DescriptorType::eStorageTexelBuffer, 32 }, + }}; + for (auto& task : tasks) { // Create command buffers - vk::CommandBufferAllocateInfo buffer_info - ( - command_pool.get(), - vk::CommandBufferLevel::ePrimary, - 1, task.command_buffer - ); - + vk::CommandBufferAllocateInfo buffer_info{ + command_pool.get(), vk::CommandBufferLevel::ePrimary, 1, task.command_buffer + }; task.command_buffer = device.allocateCommandBuffers(buffer_info)[0]; // Create staging buffer task.staging.Create(staging_info); - } - // Create present semaphore - present_semaphore = device.createSemaphoreUnique({}); + // Create descriptor pool + vk::DescriptorPoolCreateInfo pool_create_info({}, 1024, pool_sizes); + task.desc_pool = device.createDescriptorPoolUnique(pool_create_info); + } // Activate the first task. BeginTask(); @@ -74,6 +74,14 @@ bool VKTaskScheduler::Create() { return true; } +vk::CommandBuffer VKTaskScheduler::GetCommandBuffer() const { + return tasks[current_task].command_buffer; +} + +vk::DescriptorSet VKTaskScheduler::GetDescriptorSet(u32 index) const { + return tasks[current_task].descriptor_sets[index].get(); +} + void VKTaskScheduler::SyncToGPU(u64 task_index) { // No need to sync if the GPU already has finished the task if (tasks[task_index].task_id <= GetGPUTick()) { @@ -106,7 +114,7 @@ void VKTaskScheduler::SyncToGPU() { SyncToGPU(current_task); } -void VKTaskScheduler::Submit(bool present, bool wait_completion) { +void VKTaskScheduler::Submit(bool wait_completion) { // End the current task recording. auto& task = tasks[current_task]; task.command_buffer.end(); @@ -114,24 +122,12 @@ void VKTaskScheduler::Submit(bool present, bool wait_completion) { // When the task completes the timeline will increment to the task id vk::TimelineSemaphoreSubmitInfo timeline_info({}, task.task_id); - std::array signal_semaphores = { timeline.get(), present_semaphore.get() }; vk::PipelineStageFlags wait_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput; - vk::SubmitInfo submit_info({}, wait_stage, task.command_buffer, signal_semaphores, &timeline_info); - - // Wait for new swapchain image - if (present) { - auto available = swapchain->AcquireNextImage(); - submit_info.setWaitSemaphores(available); - } + vk::SubmitInfo submit_info({}, wait_stage, task.command_buffer, timeline.get(), &timeline_info); // Submit the command buffer g_vk_instace->GetGraphicsQueue().submit(submit_info); - // Present the image when rendering has finished - if (present) { - swapchain->Present(present_semaphore.get()); - } - // Block host until the GPU catches up if (wait_completion) { SyncToGPU(); diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.h b/src/video_core/renderer_vulkan/vk_task_scheduler.h index 6b0805b60..b68153dc5 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.h @@ -33,16 +33,18 @@ class VKSwapChain; /// frame latency if the CPU is too far ahead of the GPU class VKTaskScheduler { public: - explicit VKTaskScheduler(VKSwapChain* swapchain); + VKTaskScheduler() = default; ~VKTaskScheduler(); /// Create and initialize the work scheduler bool Create(); /// Retrieve either of the current frame's command buffers - vk::CommandBuffer GetCommandBuffer() const { return tasks[current_task].command_buffer; } - VKBuffer& GetStaging() { return tasks[current_task].staging; } + vk::CommandBuffer GetCommandBuffer() const; + vk::DescriptorSet GetDescriptorSet(u32 index) const; + std::tuple RequestStaging(u32 size); + VKBuffer& GetStaging() { return tasks[current_task].staging; } /// Returns the task id that the CPU is recording u64 GetCPUTick() const { return current_task_id; } @@ -58,18 +60,19 @@ public: void Schedule(std::function func); /// Submit the current work batch and move to the next frame - void Submit(bool present = true, bool wait_completion = false); + void Submit(bool wait_completion = false); private: void BeginTask(); private: struct Task { - u64 task_id{}; - std::vector> cleanups; - vk::CommandBuffer command_buffer; VKBuffer staging; - u32 current_offset{}; + u64 current_offset{}, task_id{}; + vk::CommandBuffer command_buffer; + std::vector descriptor_sets; + vk::UniqueDescriptorPool desc_pool; + std::vector> cleanups; }; vk::UniqueSemaphore timeline; @@ -78,11 +81,7 @@ private: // Each task contains unique resources std::array tasks; - u32 current_task = CONCURRENT_TASK_COUNT - 1; - - // Presentation semaphore - vk::UniqueSemaphore present_semaphore; - VKSwapChain* swapchain = nullptr; + u64 current_task = 0; }; extern std::unique_ptr g_vk_task_scheduler; diff --git a/src/video_core/renderer_vulkan/vk_texture.cpp b/src/video_core/renderer_vulkan/vk_texture.cpp index 9160819a5..6c6f58270 100644 --- a/src/video_core/renderer_vulkan/vk_texture.cpp +++ b/src/video_core/renderer_vulkan/vk_texture.cpp @@ -10,49 +10,31 @@ namespace Vulkan { -VKTexture::~VKTexture() { - if (texture) { - // Make sure to unbind the texture before destroying it - auto& state = VulkanState::Get(); - state.UnbindTexture(this); - - auto deleter = [this]() { - auto& device = g_vk_instace->GetDevice(); - device.destroyImage(texture); - device.destroyImageView(view); - device.freeMemory(memory); - }; - - // Schedule deletion of the texture after it's no longer used - // by the GPU - g_vk_task_scheduler->Schedule(deleter); +static int BytesPerPixel(vk::Format format) { + switch (format) { + case vk::Format::eR8G8B8A8Uint: + return 4; + case vk::Format::eR8G8B8Uint: + return 3; + case vk::Format::eR5G6B5UnormPack16: + case vk::Format::eR5G5B5A1UnormPack16: + case vk::Format::eR4G4B4A4UnormPack16: + return 2; + default: + UNREACHABLE(); } } -VKTexture& VKTexture::operator=(VKTexture&& move) { - +VKTexture::~VKTexture() { + Destroy(); } void VKTexture::Create(const VKTexture::Info& create_info) { auto& device = g_vk_instace->GetDevice(); info = create_info; - switch (info.format) - { - case vk::Format::eR8G8B8A8Uint: - case vk::Format::eR8G8B8A8Srgb: - case vk::Format::eR32Uint: - channels = 4; - break; - case vk::Format::eR8G8B8Uint: - channels = 3; - break; - default: - LOG_CRITICAL(Render_Vulkan, "Unknown texture format {}", info.format); - } - // Create the texture - image_size = info.width * info.height * channels; + image_size = info.width * info.height * BytesPerPixel(info.format); vk::ImageCreateFlags flags{}; if (info.view_type == vk::ImageViewType::eCube) { @@ -71,7 +53,8 @@ void VKTexture::Create(const VKTexture::Info& create_info) { // Create texture memory auto requirements = device.getImageMemoryRequirements(texture); - auto memory_index = VKBuffer::FindMemoryType(requirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal); + auto memory_index = VKBuffer::FindMemoryType(requirements.memoryTypeBits, + vk::MemoryPropertyFlagBits::eDeviceLocal); vk::MemoryAllocateInfo alloc_info(requirements.size, memory_index); memory = device.allocateMemory(alloc_info); @@ -86,6 +69,25 @@ void VKTexture::Create(const VKTexture::Info& create_info) { view = device.createImageView(view_info); } +void VKTexture::Destroy() { + if (texture) { + // Make sure to unbind the texture before destroying it + auto& state = VulkanState::Get(); + state.UnbindTexture(*this); + + auto deleter = [this]() { + auto& device = g_vk_instace->GetDevice(); + device.destroyImage(texture); + device.destroyImageView(view); + device.freeMemory(memory); + }; + + // Schedule deletion of the texture after it's no longer used + // by the GPU + g_vk_task_scheduler->Schedule(deleter); + } +} + void VKTexture::Transition(vk::ImageLayout new_layout) { if (new_layout == layout) { return; @@ -161,9 +163,8 @@ void VKTexture::Transition(vk::ImageLayout new_layout) { vk::ImageSubresourceRange(info.aspect, 0, 1, 0, 1) }; - std::array barriers{ barrier }; auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); - command_buffer.pipelineBarrier(source.stage, dst.stage, vk::DependencyFlagBits::eByRegion, {}, {}, barriers); + command_buffer.pipelineBarrier(source.stage, dst.stage, vk::DependencyFlagBits::eByRegion, {}, {}, barrier); layout = new_layout; } @@ -173,9 +174,8 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region, LOG_ERROR(Render_Vulkan, "Cannot upload pixels without staging buffer!"); } - auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); - // Copy pixels to staging buffer + auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); std::memcpy(buffer, pixels.data(), pixels.size()); vk::BufferImageCopy copy_region{ @@ -185,6 +185,10 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region, {region.extent.width, region.extent.height, 1} }; + // Exit rendering for transfer operations + auto& state = VulkanState::Get(); + state.EndRendering(); + // Transition image to transfer format auto old_layout = GetLayout(); Transition(vk::ImageLayout::eTransferDstOptimal); @@ -213,6 +217,10 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region {region.extent.width, region.extent.height, 1} }; + // Exit rendering for transfer operations + auto& state = VulkanState::Get(); + state.EndRendering(); + // Transition image to transfer format auto old_layout = GetLayout(); Transition(vk::ImageLayout::eTransferSrcOptimal); @@ -223,7 +231,7 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region // Wait for the data to be available // NOTE: This is really slow and should be reworked - g_vk_task_scheduler->Submit(false, true); + g_vk_task_scheduler->Submit(true); std::memcpy(memory.data(), buffer, memory.size_bytes()); // Restore layout diff --git a/src/video_core/renderer_vulkan/vk_texture.h b/src/video_core/renderer_vulkan/vk_texture.h index f95976019..3f9b1e723 100644 --- a/src/video_core/renderer_vulkan/vk_texture.h +++ b/src/video_core/renderer_vulkan/vk_texture.h @@ -14,12 +14,6 @@ namespace Vulkan { -struct SamplerInfo { - std::array wrapping{}; - vk::Filter min_filter{}, mag_filter{}; - vk::SamplerMipmapMode mipmap_mode{}; -}; - /// Vulkan texture object class VKTexture final : public NonCopyable { public: @@ -33,7 +27,6 @@ public: vk::ImageAspectFlags aspect; u32 multisamples = 1; u32 levels = 1, layers = 1; - SamplerInfo sampler_info = {}; }; VKTexture() = default; @@ -44,6 +37,8 @@ public: /// Create a new Vulkan texture object void Create(const VKTexture::Info& info); + void Adopt + void Destroy(); /// Query objects bool IsValid() const { return texture; } @@ -68,7 +63,8 @@ private: vk::Image texture; vk::ImageView view; vk::DeviceMemory memory; - u32 channels{}, image_size{}; + u32 image_size{}; + bool adopted = false; }; } // namespace Vulkan