renderer_vulkan: Initial rasterizer cache vulkan port
This commit is contained in:
@@ -58,6 +58,7 @@ protected:
|
||||
|
||||
// Enable std::move operations
|
||||
NonCopyable(NonCopyable&&) = default;
|
||||
NonCopyable& operator=(NonCopyable&&) = default;
|
||||
|
||||
NonCopyable(const NonCopyable&) = delete;
|
||||
NonCopyable& operator=(const NonCopyable&) = delete;
|
||||
|
@@ -73,6 +73,8 @@ add_library(video_core STATIC
|
||||
renderer_vulkan/renderer_vulkan.h
|
||||
renderer_vulkan/vk_buffer.cpp
|
||||
renderer_vulkan/vk_buffer.h
|
||||
renderer_vulkan/vk_format_reinterpreter.cpp
|
||||
renderer_vulkan/vk_format_reinterpreter.h
|
||||
renderer_vulkan/vk_instance.cpp
|
||||
renderer_vulkan/vk_instance.h
|
||||
renderer_vulkan/vk_pipeline_builder.cpp
|
||||
|
@@ -9,7 +9,6 @@
|
||||
#include "common/math_util.h"
|
||||
#include "core/hw/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
#include "video_core/renderer_vulkan/vk_state.h"
|
||||
|
||||
|
398
src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp
Normal file
398
src/video_core/renderer_vulkan/vk_format_reinterpreter.cpp
Normal file
@@ -0,0 +1,398 @@
|
||||
// Copyright 2020 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "video_core/renderer_vulkan/vk_format_reinterpreter.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_state.h"
|
||||
|
||||
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<u32>& src_rect,
|
||||
Surface dst_surface, const Common::Rectangle<u32>& 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<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(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<u32>& src_rect, GLuint read_fb_handle,
|
||||
GLuint dst_tex, const Common::Rectangle<u32>& 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<GLsizeiptr>(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<GLint>(src_rect.left), static_cast<GLint>(src_rect.bottom),
|
||||
static_cast<GLsizei>(src_rect.GetWidth()),
|
||||
static_cast<GLsizei>(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<GLint>(dst_rect.left);
|
||||
state.viewport.y = static_cast<GLint>(dst_rect.bottom);
|
||||
state.viewport.width = static_cast<GLsizei>(dst_rect.GetWidth());
|
||||
state.viewport.height = static_cast<GLsizei>(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<GLfloat>(src_rect.GetWidth()),
|
||||
static_cast<GLfloat>(src_rect.GetHeight()));
|
||||
glUniform4f(d24s8_abgr_viewport_u_id, static_cast<GLfloat>(state.viewport.x),
|
||||
static_cast<GLfloat>(state.viewport.y),
|
||||
static_cast<GLfloat>(state.viewport.width),
|
||||
static_cast<GLfloat>(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<const char*>(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<u32>& src_rect, GLuint read_fb_handle,
|
||||
GLuint dst_tex, const Common::Rectangle<u32>& 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<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.bottom),
|
||||
static_cast<GLsizei>(dst_rect.GetWidth()),
|
||||
static_cast<GLsizei>(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<u32> temp_rect{0, 0, 0, 0};
|
||||
};
|
||||
|
||||
FormatReinterpreterOpenGL::FormatReinterpreterOpenGL() {
|
||||
const std::string_view vendor{reinterpret_cast<const char*>(glGetString(GL_VENDOR))};
|
||||
const std::string_view version{reinterpret_cast<const char*>(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<ShaderD24S8toRGBA8>());
|
||||
LOG_INFO(Render_OpenGL, "Using shader for D24S8 to RGBA8 reinterpretation");
|
||||
} else {
|
||||
reinterpreters.emplace(PixelFormatPair{PixelFormat::RGBA8, PixelFormat::D24S8},
|
||||
std::make_unique<PixelBufferD24S8toABGR>());
|
||||
LOG_INFO(Render_OpenGL, "Using pbo for D24S8 to RGBA8 reinterpretation");
|
||||
}
|
||||
reinterpreters.emplace(PixelFormatPair{PixelFormat::RGB5A1, PixelFormat::RGBA4},
|
||||
std::make_unique<RGBA4toRGB5A1>());
|
||||
}
|
||||
|
||||
FormatReinterpreterOpenGL::~FormatReinterpreterOpenGL() = default;
|
||||
|
||||
std::pair<FormatReinterpreterOpenGL::ReinterpreterMap::iterator,
|
||||
FormatReinterpreterOpenGL::ReinterpreterMap::iterator>
|
||||
FormatReinterpreterOpenGL::GetPossibleReinterpretations(PixelFormat dst_format) {
|
||||
return reinterpreters.equal_range(dst_format);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
63
src/video_core/renderer_vulkan/vk_format_reinterpreter.h
Normal file
63
src/video_core/renderer_vulkan/vk_format_reinterpreter.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <glad/glad.h>
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "video_core/renderer_vulkan/vk_surface_params.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class RasterizerCacheVulkan;
|
||||
|
||||
struct CachedSurface;
|
||||
using Surface = std::shared_ptr<CachedSurface>;
|
||||
|
||||
struct PixelFormatPair {
|
||||
const SurfaceParams::PixelFormat dst_format, src_format;
|
||||
struct less {
|
||||
using is_transparent = void;
|
||||
constexpr bool operator()(PixelFormatPair lhs, PixelFormatPair rhs) const {
|
||||
return std::tie(lhs.dst_format, lhs.src_format) <
|
||||
std::tie(rhs.dst_format, rhs.src_format);
|
||||
}
|
||||
constexpr bool operator()(SurfaceParams::PixelFormat lhs,
|
||||
PixelFormatPair rhs) const {
|
||||
return lhs < rhs.dst_format;
|
||||
}
|
||||
constexpr bool operator()(PixelFormatPair lhs,
|
||||
SurfaceParams::PixelFormat rhs) const {
|
||||
return lhs.dst_format < rhs;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class FormatReinterpreterBase {
|
||||
public:
|
||||
virtual ~FormatReinterpreterBase() = default;
|
||||
|
||||
virtual void Reinterpret(Surface src_surface, const Common::Rectangle<u32>& src_rect,
|
||||
Surface dst_surface, const Common::Rectangle<u32>& dst_rect) = 0;
|
||||
};
|
||||
|
||||
class FormatReinterpreterVulkan : NonCopyable {
|
||||
using ReinterpreterMap =
|
||||
std::map<PixelFormatPair, std::unique_ptr<FormatReinterpreterBase>, PixelFormatPair::less>;
|
||||
|
||||
public:
|
||||
explicit FormatReinterpreterVulkan();
|
||||
~FormatReinterpreterVulkan();
|
||||
|
||||
std::pair<ReinterpreterMap::iterator, ReinterpreterMap::iterator> GetPossibleReinterpretations(
|
||||
SurfaceParams::PixelFormat dst_format);
|
||||
|
||||
private:
|
||||
ReinterpreterMap reinterpreters;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
@@ -277,7 +277,15 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
surfaces_rect.bottom, surfaces_rect.top))}; // Bottom
|
||||
|
||||
// Bind the framebuffer surfaces
|
||||
state.PushRenderTargets(&color_surface->texture, &depth_surface->texture);
|
||||
Attachment color = {
|
||||
.image = &color_surface->texture,
|
||||
};
|
||||
|
||||
Attachment depth = {
|
||||
.image = &depth_surface->texture,
|
||||
};
|
||||
|
||||
state.BeginRendering(color, depth);
|
||||
|
||||
// Sync the viewport
|
||||
vk::Viewport viewport(0, 0, viewport_rect_unscaled.GetWidth() * res_scale,
|
||||
|
@@ -28,7 +28,9 @@
|
||||
#include "core/settings.h"
|
||||
#include "video_core/pica_state.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_format_reinterpreter.h"
|
||||
#include "video_core/renderer_vulkan/vk_state.h"
|
||||
#include "video_core/utils.h"
|
||||
#include "video_core/video_core.h"
|
||||
@@ -46,6 +48,26 @@ static constexpr std::array<vk::Format, 5> fb_format_tuples = {{
|
||||
vk::Format::eR4G4B4A4UnormPack16, // RGBA4
|
||||
}};
|
||||
|
||||
static constexpr std::array<vk::Format, 4> depth_format_tuples = {{
|
||||
vk::Format::eD16Unorm, // D16
|
||||
vk::Format::eUndefined,
|
||||
vk::Format::eD24UnormS8Uint, // D24
|
||||
vk::Format::eD24UnormS8Uint, // D24S8
|
||||
}};
|
||||
|
||||
vk::Format GetFormatTuple(PixelFormat pixel_format) {
|
||||
const SurfaceType type = SurfaceParams::GetFormatType(pixel_format);
|
||||
if (type == SurfaceType::Color) {
|
||||
ASSERT(static_cast<std::size_t>(pixel_format) < fb_format_tuples.size());
|
||||
return fb_format_tuples[static_cast<unsigned int>(pixel_format)];
|
||||
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
|
||||
std::size_t tuple_idx = static_cast<std::size_t>(pixel_format) - 14;
|
||||
ASSERT(tuple_idx < depth_format_tuples.size());
|
||||
return depth_format_tuples[tuple_idx];
|
||||
}
|
||||
return vk::Format::eR8G8B8A8Unorm;
|
||||
}
|
||||
|
||||
template <typename Map, typename Interval>
|
||||
static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
|
||||
return boost::make_iterator_range(map.equal_range(interval));
|
||||
@@ -205,6 +227,68 @@ static constexpr std::array<void (*)(u32, u32, u8*, PAddr, PAddr, PAddr), 18> gp
|
||||
MortonCopy<false, PixelFormat::D24S8> // 17
|
||||
};
|
||||
|
||||
inline vk::ImageSubresourceRange SubResourceLayersToRange(const vk::ImageSubresourceLayers& in) {
|
||||
vk::ImageSubresourceRange out;
|
||||
out.aspectMask = in.aspectMask;
|
||||
out.baseArrayLayer = in.baseArrayLayer;
|
||||
out.layerCount = in.layerCount;
|
||||
out.baseMipLevel = in.mipLevel;
|
||||
out.levelCount = 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool BlitTextures(const Surface& src_surface, const Common::Rectangle<u32>& src_rect,
|
||||
const Surface& dst_surface, const Common::Rectangle<u32>& dst_rect, SurfaceType type) {
|
||||
vk::ImageSubresourceLayers image_range({}, {}, 0, 1);
|
||||
switch (src_surface->type) {
|
||||
case SurfaceParams::SurfaceType::Color:
|
||||
case SurfaceParams::SurfaceType::Texture:
|
||||
image_range.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
break;
|
||||
case SurfaceParams::SurfaceType::Depth:
|
||||
image_range.aspectMask = vk::ImageAspectFlagBits::eDepth;
|
||||
break;
|
||||
case SurfaceParams::SurfaceType::DepthStencil:
|
||||
image_range.aspectMask =
|
||||
vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
// Prepare images for transfer
|
||||
auto old_src_layout = src_surface->texture.GetLayout();
|
||||
auto old_dst_layout = dst_surface->texture.GetLayout();
|
||||
|
||||
src_surface->texture.Transition(vk::ImageLayout::eTransferSrcOptimal);
|
||||
dst_surface->texture.Transition(vk::ImageLayout::eTransferDstOptimal);
|
||||
|
||||
vk::ImageBlit blit_area;
|
||||
blit_area.srcSubresource = image_range;
|
||||
blit_area.srcOffsets[0] = vk::Offset3D(src_rect.left, src_rect.bottom, 0);
|
||||
blit_area.srcOffsets[1] = vk::Offset3D(src_rect.right, src_rect.top, 1);
|
||||
blit_area.dstSubresource = image_range;
|
||||
blit_area.dstOffsets[0] = vk::Offset3D(dst_rect.left, dst_rect.bottom, 0);
|
||||
blit_area.dstOffsets[1] = vk::Offset3D(dst_rect.right, dst_rect.top, 1);
|
||||
|
||||
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||
command_buffer.blitImage(src_surface->texture.GetHandle(), vk::ImageLayout::eTransferSrcOptimal,
|
||||
dst_surface->texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal,
|
||||
{blit_area}, vk::Filter::eNearest);
|
||||
|
||||
// Revert changes to the layout
|
||||
src_surface->texture.Transition(old_src_layout);
|
||||
dst_surface->texture.Transition(old_dst_layout);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static vk::Rect2D FromRect(Common::Rectangle<u32> rect) {
|
||||
vk::Offset2D offset{static_cast<s32>(rect.left), static_cast<s32>(rect.bottom)};
|
||||
vk::Extent2D extent{rect.GetWidth(), rect.GetHeight()};
|
||||
return vk::Rect2D{offset, extent};
|
||||
}
|
||||
|
||||
// Allocate an uninitialized texture of appropriate size and format for the surface
|
||||
VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(vk::Format format, u32 width, u32 height)
|
||||
{
|
||||
@@ -224,7 +308,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(vk::Format format, u32 w
|
||||
.format = format,
|
||||
.type = vk::ImageType::e2D,
|
||||
.view_type = vk::ImageViewType::e2D,
|
||||
.mipmap_levels = levels
|
||||
.levels = levels
|
||||
};
|
||||
|
||||
VKTexture texture;
|
||||
@@ -303,10 +387,11 @@ void RasterizerCacheVulkan::CopySurface(const Surface& src_surface, const Surfac
|
||||
if (src_surface->CanSubRect(subrect_params)) {
|
||||
auto srect = src_surface->GetScaledSubRect(subrect_params);
|
||||
auto drect = dst_surface->GetScaledSubRect(subrect_params);
|
||||
src_surface->texture.BlitTo(srect, &dst_surface->texture, drect, src_surface->type);
|
||||
|
||||
BlitTextures(src_surface, srect, dst_surface, drect, src_surface->type);
|
||||
return;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -426,88 +511,23 @@ void CachedSurface::FlushGPUBuffer(PAddr flush_start, PAddr flush_end) {
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 192, 64));
|
||||
MICROPROFILE_DEFINE(Vulkan_TextureUL, "Vulkan", "Texture Upload", MP_RGB(128, 192, 64));
|
||||
void CachedSurface::UploadGPUTexture(Common::Rectangle<u32> rect) {
|
||||
if (type == SurfaceType::Fill)
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_TextureUL);
|
||||
MICROPROFILE_SCOPE(Vulkan_TextureUL);
|
||||
|
||||
ASSERT(vk_buffer.size() == width * height * GetBytesPerPixel(pixel_format));
|
||||
|
||||
u64 tex_hash = 0;
|
||||
|
||||
if (Settings::values.dump_textures || Settings::values.custom_textures) {
|
||||
tex_hash = Common::ComputeHash64(vk_buffer.data(), vk_buffer.size());
|
||||
}
|
||||
|
||||
if (Settings::values.custom_textures) {
|
||||
is_custom = LoadCustomTexture(tex_hash);
|
||||
}
|
||||
// TODO: Handle resolution scaling and custom textures
|
||||
|
||||
// Load data from memory to the surface
|
||||
int x0 = static_cast<int>(rect.left);
|
||||
int y0 = static_cast<int>(rect.bottom);
|
||||
std::size_t buffer_offset = (y0 * stride + x0) * GetBytesPerPixel(pixel_format);
|
||||
auto buffer_offset = (rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format);
|
||||
auto update_size = rect.GetWidth() * rect.GetHeight() * GetBytesPerPixel(pixel_format);
|
||||
std::span<u8> memory(vk_buffer.data() + buffer_offset, update_size);
|
||||
|
||||
//const FormatTuple& tuple = GetFormatTuple(pixel_format);
|
||||
//GLuint target_tex = texture.handle;
|
||||
|
||||
// If not 1x scale, create 1x texture that we will blit from to replace texture subrect in
|
||||
// surface
|
||||
VKTexture unscaled_tex;
|
||||
if (res_scale != 1) {
|
||||
x0 = 0;
|
||||
y0 = 0;
|
||||
|
||||
VKTexture::Info info = {
|
||||
.width = rect.GetWidth(),
|
||||
.height = rect.GetHeight(),
|
||||
.format = fb_format_tuples[static_cast<u32>(pixel_format)],
|
||||
.type = vk::ImageType::e2D,
|
||||
.view_type = vk::ImageViewType::e2D
|
||||
};
|
||||
|
||||
unscaled_tex.Create(info);
|
||||
}
|
||||
|
||||
//OpenGLState cur_state = OpenGLState::GetCurState();
|
||||
|
||||
//GLuint old_tex = cur_state.texture_units[0].texture_2d;
|
||||
//cur_state.texture_units[0].texture_2d = target_tex;
|
||||
//cur_state.Apply();
|
||||
|
||||
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
|
||||
//ASSERT(stride * GetBytesPerPixel(pixel_format) % 4 == 0);
|
||||
//glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
|
||||
|
||||
//glActiveTexture(GL_TEXTURE0);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
&gl_buffer[buffer_offset]);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
//if (Settings::values.dump_textures && !is_custom)
|
||||
// DumpTexture(target_tex, tex_hash);
|
||||
|
||||
cur_state.texture_units[0].texture_2d = old_tex;
|
||||
cur_state.Apply();
|
||||
|
||||
if (res_scale != 1) {
|
||||
auto scaled_rect = rect;
|
||||
scaled_rect.left *= res_scale;
|
||||
scaled_rect.top *= res_scale;
|
||||
scaled_rect.right *= res_scale;
|
||||
scaled_rect.bottom *= res_scale;
|
||||
auto from_rect =
|
||||
is_custom ? Common::Rectangle<u32>{0, custom_tex_info.height, custom_tex_info.width, 0}
|
||||
: Common::Rectangle<u32>{0, rect.GetHeight(), rect.GetWidth(), 0};
|
||||
if (!owner.texture_filterer->Filter(unscaled_tex.handle, from_rect, texture.handle,
|
||||
scaled_rect, type, read_fb_handle, draw_fb_handle)) {
|
||||
BlitTextures(unscaled_tex.handle, from_rect, texture.handle, scaled_rect, type,
|
||||
read_fb_handle, draw_fb_handle);
|
||||
}
|
||||
}
|
||||
texture.Upload(0, 0, stride, FromRect(rect), memory);
|
||||
|
||||
InvalidateAllWatcher();
|
||||
}
|
||||
@@ -520,87 +540,17 @@ void CachedSurface::DownloadGPUTexture(const Common::Rectangle<u32>& rect) {
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_TextureDL);
|
||||
|
||||
if (gl_buffer.empty()) {
|
||||
gl_buffer.resize(width * height * GetGLBytesPerPixel(pixel_format));
|
||||
if (vk_buffer.empty()) {
|
||||
vk_buffer.resize(width * height * GetBytesPerPixel(pixel_format));
|
||||
}
|
||||
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
OpenGLState prev_state = state;
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
// TODO: Handle resolution scaling and custom textures
|
||||
|
||||
const FormatTuple& tuple = GetFormatTuple(pixel_format);
|
||||
auto buffer_offset = (rect.bottom * stride + rect.left) * GetBytesPerPixel(pixel_format);
|
||||
auto buffer_size = rect.GetWidth() * rect.GetHeight() * GetBytesPerPixel(pixel_format);
|
||||
std::span<u8> memory(vk_buffer.data() + buffer_offset, buffer_size);
|
||||
|
||||
// Ensure no bad interactions with GL_PACK_ALIGNMENT
|
||||
ASSERT(stride * GetGLBytesPerPixel(pixel_format) % 4 == 0);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(stride));
|
||||
std::size_t buffer_offset =
|
||||
(rect.bottom * stride + rect.left) * GetGLBytesPerPixel(pixel_format);
|
||||
|
||||
// If not 1x scale, blit scaled texture to a new 1x texture and use that to flush
|
||||
if (res_scale != 1) {
|
||||
auto scaled_rect = rect;
|
||||
scaled_rect.left *= res_scale;
|
||||
scaled_rect.top *= res_scale;
|
||||
scaled_rect.right *= res_scale;
|
||||
scaled_rect.bottom *= res_scale;
|
||||
|
||||
Common::Rectangle<u32> unscaled_tex_rect{0, rect.GetHeight(), rect.GetWidth(), 0};
|
||||
OGLTexture unscaled_tex =
|
||||
owner.AllocateSurfaceTexture(tuple, rect.GetWidth(), rect.GetHeight());
|
||||
BlitTextures(texture.handle, scaled_rect, unscaled_tex.handle, unscaled_tex_rect, type,
|
||||
read_fb_handle, draw_fb_handle);
|
||||
|
||||
state.texture_units[0].texture_2d = unscaled_tex.handle;
|
||||
state.Apply();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
if (GLES) {
|
||||
owner.texture_downloader_es->GetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type,
|
||||
rect.GetHeight(), rect.GetWidth(),
|
||||
&gl_buffer[buffer_offset]);
|
||||
} else {
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, &gl_buffer[buffer_offset]);
|
||||
}
|
||||
} else {
|
||||
state.ResetTexture(texture.handle);
|
||||
state.draw.read_framebuffer = read_fb_handle;
|
||||
state.Apply();
|
||||
|
||||
if (type == SurfaceType::Color || type == SurfaceType::Texture) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
} else if (type == SurfaceType::Depth) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
} else {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
texture.handle, 0);
|
||||
}
|
||||
switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) {
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||
LOG_WARNING(Render_OpenGL, "Framebuffer incomplete attachment");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
|
||||
LOG_WARNING(Render_OpenGL, "Framebuffer incomplete dimensions");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
||||
LOG_WARNING(Render_OpenGL, "Framebuffer incomplete missing attachment");
|
||||
break;
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED:
|
||||
LOG_WARNING(Render_OpenGL, "Framebuffer unsupported");
|
||||
break;
|
||||
}
|
||||
glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom),
|
||||
static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()),
|
||||
tuple.format, tuple.type, &gl_buffer[buffer_offset]);
|
||||
}
|
||||
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
texture.Download(0, 0, stride, FromRect(rect), memory);
|
||||
}
|
||||
|
||||
enum MatchFlags {
|
||||
@@ -708,12 +658,7 @@ static Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams&
|
||||
|
||||
RasterizerCacheVulkan::RasterizerCacheVulkan() {
|
||||
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
|
||||
texture_filterer = std::make_unique<TextureFilterer>(Settings::values.texture_filter_name,
|
||||
resolution_scale_factor);
|
||||
format_reinterpreter = std::make_unique<FormatReinterpreterOpenGL>();
|
||||
|
||||
read_framebuffer.Create();
|
||||
draw_framebuffer.Create();
|
||||
//format_reinterpreter = std::make_unique<FormatReinterpreterVulkan>();
|
||||
}
|
||||
|
||||
RasterizerCacheVulkan::~RasterizerCacheVulkan() {
|
||||
@@ -734,10 +679,7 @@ bool RasterizerCacheVulkan::BlitSurfaces(const Surface& src_surface,
|
||||
return false;
|
||||
|
||||
dst_surface->InvalidateAllWatcher();
|
||||
|
||||
return BlitTextures(src_surface->texture.handle, src_rect, dst_surface->texture.handle,
|
||||
dst_rect, src_surface->type, read_framebuffer.handle,
|
||||
draw_framebuffer.handle);
|
||||
return BlitTextures(src_surface, src_rect, dst_surface, dst_rect, src_surface->type);
|
||||
}
|
||||
|
||||
Surface RasterizerCacheVulkan::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
|
||||
@@ -886,18 +828,19 @@ Surface RasterizerCacheVulkan::GetTextureSurface(const Pica::Texture::TextureInf
|
||||
params.height = info.height;
|
||||
params.is_tiled = true;
|
||||
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format);
|
||||
params.res_scale = texture_filterer->IsNull() ? 1 : resolution_scale_factor;
|
||||
//params.res_scale = texture_filterer->IsNull() ? 1 : resolution_scale_factor;
|
||||
params.res_scale = 1;
|
||||
params.UpdateParams();
|
||||
|
||||
u32 min_width = info.width >> max_level;
|
||||
u32 min_height = info.height >> max_level;
|
||||
if (min_width % 8 != 0 || min_height % 8 != 0) {
|
||||
LOG_CRITICAL(Render_OpenGL, "Texture size ({}x{}) is not multiple of 8", min_width,
|
||||
LOG_CRITICAL(Render_Vulkan, "Texture size ({}x{}) is not multiple of 8", min_width,
|
||||
min_height);
|
||||
return nullptr;
|
||||
}
|
||||
if (info.width != (min_width << max_level) || info.height != (min_height << max_level)) {
|
||||
LOG_CRITICAL(Render_OpenGL,
|
||||
LOG_CRITICAL(Render_Vulkan,
|
||||
"Texture size ({}x{}) does not support required mipmap level ({})",
|
||||
params.width, params.height, max_level);
|
||||
return nullptr;
|
||||
@@ -912,49 +855,10 @@ Surface RasterizerCacheVulkan::GetTextureSurface(const Pica::Texture::TextureInf
|
||||
if (max_level >= 8) {
|
||||
// since PICA only supports texture size between 8 and 1024, there are at most eight
|
||||
// possible mipmap levels including the base.
|
||||
LOG_CRITICAL(Render_OpenGL, "Unsupported mipmap level {}", max_level);
|
||||
LOG_CRITICAL(Render_Vulkan, "Unsupported mipmap level {}", max_level);
|
||||
return nullptr;
|
||||
}
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
OpenGLState state;
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
auto format_tuple = GetFormatTuple(params.pixel_format);
|
||||
|
||||
// Allocate more mipmap level if necessary
|
||||
if (surface->max_level < max_level) {
|
||||
state.texture_units[0].texture_2d = surface->texture.handle;
|
||||
state.Apply();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level);
|
||||
u32 width;
|
||||
u32 height;
|
||||
if (surface->is_custom) {
|
||||
width = surface->custom_tex_info.width;
|
||||
height = surface->custom_tex_info.height;
|
||||
} else {
|
||||
width = surface->GetScaledWidth();
|
||||
height = surface->GetScaledHeight();
|
||||
}
|
||||
// If we are using ARB_texture_storage then we've already allocated all of the mipmap
|
||||
// levels
|
||||
if (!GL_ARB_texture_storage) {
|
||||
for (u32 level = surface->max_level + 1; level <= max_level; ++level) {
|
||||
glTexImage2D(GL_TEXTURE_2D, level, format_tuple.internal_format, width >> level,
|
||||
height >> level, 0, format_tuple.format, format_tuple.type,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
if (surface->is_custom || !texture_filterer->IsNull()) {
|
||||
// TODO: proper mipmap support for custom textures
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
surface->max_level = max_level;
|
||||
}
|
||||
|
||||
// Blit mipmaps that have been invalidated
|
||||
state.draw.read_framebuffer = read_framebuffer.handle;
|
||||
state.draw.draw_framebuffer = draw_framebuffer.handle;
|
||||
state.ResetTexture(surface->texture.handle);
|
||||
SurfaceParams surface_params = *surface;
|
||||
for (u32 level = 1; level <= max_level; ++level) {
|
||||
// In PICA all mipmap levels are stored next to each other
|
||||
@@ -979,24 +883,13 @@ Surface RasterizerCacheVulkan::GetTextureSurface(const Pica::Texture::TextureInf
|
||||
if (!level_surface->invalid_regions.empty()) {
|
||||
ValidateSurface(level_surface, level_surface->addr, level_surface->size);
|
||||
}
|
||||
state.ResetTexture(level_surface->texture.handle);
|
||||
state.Apply();
|
||||
if (!surface->is_custom && texture_filterer->IsNull()) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
level_surface->texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
surface->texture.handle, level);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
//state.ResetTexture(level_surface->texture.handle);
|
||||
//state.Apply();
|
||||
if (!surface->is_custom /*&& texture_filterer->IsNull()*/) {
|
||||
auto src_rect = level_surface->GetScaledRect();
|
||||
auto dst_rect = surface_params.GetScaledRect();
|
||||
glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top,
|
||||
dst_rect.left, dst_rect.bottom, dst_rect.right, dst_rect.top,
|
||||
GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
BlitSurfaces(level_surface, src_rect, surface, dst_rect);
|
||||
}
|
||||
watcher->Validate();
|
||||
}
|
||||
@@ -1006,106 +899,13 @@ Surface RasterizerCacheVulkan::GetTextureSurface(const Pica::Texture::TextureInf
|
||||
return surface;
|
||||
}
|
||||
|
||||
const CachedTextureCube& RasterizerCacheVulkan::GetTextureCube(const TextureCubeConfig& config) {
|
||||
auto& cube = texture_cube_cache[config];
|
||||
|
||||
struct Face {
|
||||
Face(std::shared_ptr<SurfaceWatcher>& watcher, PAddr address, GLenum gl_face)
|
||||
: watcher(watcher), address(address), gl_face(gl_face) {}
|
||||
std::shared_ptr<SurfaceWatcher>& watcher;
|
||||
PAddr address;
|
||||
GLenum gl_face;
|
||||
};
|
||||
|
||||
const std::array<Face, 6> faces{{
|
||||
{cube.px, config.px, GL_TEXTURE_CUBE_MAP_POSITIVE_X},
|
||||
{cube.nx, config.nx, GL_TEXTURE_CUBE_MAP_NEGATIVE_X},
|
||||
{cube.py, config.py, GL_TEXTURE_CUBE_MAP_POSITIVE_Y},
|
||||
{cube.ny, config.ny, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y},
|
||||
{cube.pz, config.pz, GL_TEXTURE_CUBE_MAP_POSITIVE_Z},
|
||||
{cube.nz, config.nz, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z},
|
||||
}};
|
||||
|
||||
for (const Face& face : faces) {
|
||||
if (!face.watcher || !face.watcher->Get()) {
|
||||
Pica::Texture::TextureInfo info;
|
||||
info.physical_address = face.address;
|
||||
info.height = info.width = config.width;
|
||||
info.format = config.format;
|
||||
info.SetDefaultStride();
|
||||
auto surface = GetTextureSurface(info);
|
||||
if (surface) {
|
||||
face.watcher = surface->CreateWatcher();
|
||||
} else {
|
||||
// Can occur when texture address is invalid. We mark the watcher with nullptr
|
||||
// in this case and the content of the face wouldn't get updated. These are
|
||||
// usually leftover setup in the texture unit and games are not supposed to draw
|
||||
// using them.
|
||||
face.watcher = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cube.texture.handle == 0) {
|
||||
for (const Face& face : faces) {
|
||||
if (face.watcher) {
|
||||
auto surface = face.watcher->Get();
|
||||
cube.res_scale = std::max(cube.res_scale, surface->res_scale);
|
||||
}
|
||||
}
|
||||
|
||||
cube.texture.Create();
|
||||
AllocateTextureCube(
|
||||
cube.texture.handle,
|
||||
GetFormatTuple(CachedSurface::PixelFormatFromTextureFormat(config.format)),
|
||||
cube.res_scale * config.width);
|
||||
}
|
||||
|
||||
u32 scaled_size = cube.res_scale * config.width;
|
||||
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.draw.read_framebuffer = read_framebuffer.handle;
|
||||
state.draw.draw_framebuffer = draw_framebuffer.handle;
|
||||
state.ResetTexture(cube.texture.handle);
|
||||
|
||||
for (const Face& face : faces) {
|
||||
if (face.watcher && !face.watcher->IsValid()) {
|
||||
auto surface = face.watcher->Get();
|
||||
if (!surface->invalid_regions.empty()) {
|
||||
ValidateSurface(surface, surface->addr, surface->size);
|
||||
}
|
||||
state.ResetTexture(surface->texture.handle);
|
||||
state.Apply();
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
surface->texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face.gl_face,
|
||||
cube.texture.handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
|
||||
auto src_rect = surface->GetScaledRect();
|
||||
glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, 0, 0,
|
||||
scaled_size, scaled_size, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
face.watcher->Validate();
|
||||
}
|
||||
}
|
||||
|
||||
return cube;
|
||||
}
|
||||
|
||||
SurfaceSurfaceRect_Tuple RasterizerCacheVulkan::GetFramebufferSurfaces(
|
||||
bool using_color_fb, bool using_depth_fb, const Common::Rectangle<s32>& viewport_rect) {
|
||||
const auto& regs = Pica::g_state.regs;
|
||||
const auto& config = regs.framebuffer.framebuffer;
|
||||
|
||||
// update resolution_scale_factor and reset cache if changed
|
||||
if ((resolution_scale_factor != VideoCore::GetResolutionScaleFactor()) |
|
||||
/*if ((resolution_scale_factor != VideoCore::GetResolutionScaleFactor()) |
|
||||
(VideoCore::g_texture_filter_update_requested.exchange(false) &&
|
||||
texture_filterer->Reset(Settings::values.texture_filter_name, resolution_scale_factor))) {
|
||||
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
|
||||
@@ -1113,7 +913,7 @@ SurfaceSurfaceRect_Tuple RasterizerCacheVulkan::GetFramebufferSurfaces(
|
||||
while (!surface_cache.empty())
|
||||
UnregisterSurface(*surface_cache.begin()->second.begin());
|
||||
texture_cube_cache.clear();
|
||||
}
|
||||
}*/
|
||||
|
||||
Common::Rectangle<u32> viewport_clamped{
|
||||
static_cast<u32>(std::clamp(viewport_rect.left, 0, static_cast<s32>(config.GetWidth()))),
|
||||
@@ -1320,9 +1120,8 @@ void RasterizerCacheVulkan::ValidateSurface(const Surface& surface, PAddr addr,
|
||||
|
||||
// Load data from 3DS memory
|
||||
FlushRegion(params.addr, params.size);
|
||||
surface->LoadGLBuffer(params.addr, params.end);
|
||||
surface->UploadGLTexture(surface->GetSubRect(params), read_framebuffer.handle,
|
||||
draw_framebuffer.handle);
|
||||
surface->LoadGPUBuffer(params.addr, params.end);
|
||||
surface->UploadGPUTexture(surface->GetSubRect(params));
|
||||
notify_validated(params.GetInterval());
|
||||
}
|
||||
}
|
||||
@@ -1385,32 +1184,7 @@ bool RasterizerCacheVulkan::ValidateByReinterpretation(const Surface& surface,
|
||||
auto src_rect = reinterpret_surface->GetScaledSubRect(reinterpret_params);
|
||||
auto dest_rect = surface->GetScaledSubRect(reinterpret_params);
|
||||
|
||||
if (!texture_filterer->IsNull() && reinterpret_surface->res_scale == 1 &&
|
||||
surface->res_scale == resolution_scale_factor) {
|
||||
// The destination surface is either a framebuffer, or a filtered texture.
|
||||
// Create an intermediate surface to convert to before blitting to the
|
||||
// destination.
|
||||
Common::Rectangle<u32> tmp_rect{0, dest_rect.GetHeight() / resolution_scale_factor,
|
||||
dest_rect.GetWidth() / resolution_scale_factor, 0};
|
||||
OGLTexture tmp_tex = AllocateSurfaceTexture(
|
||||
GetFormatTuple(reinterpreter->first.dst_format), tmp_rect.right, tmp_rect.top);
|
||||
reinterpreter->second->Reinterpret(reinterpret_surface->texture.handle, src_rect,
|
||||
read_framebuffer.handle, tmp_tex.handle,
|
||||
tmp_rect, draw_framebuffer.handle);
|
||||
SurfaceParams::SurfaceType type =
|
||||
SurfaceParams::GetFormatType(reinterpreter->first.dst_format);
|
||||
|
||||
if (!texture_filterer->Filter(tmp_tex.handle, tmp_rect, surface->texture.handle,
|
||||
dest_rect, type, read_framebuffer.handle,
|
||||
draw_framebuffer.handle)) {
|
||||
BlitTextures(tmp_tex.handle, tmp_rect, surface->texture.handle, dest_rect, type,
|
||||
read_framebuffer.handle, draw_framebuffer.handle);
|
||||
}
|
||||
} else {
|
||||
reinterpreter->second->Reinterpret(reinterpret_surface->texture.handle, src_rect,
|
||||
read_framebuffer.handle, surface->texture.handle,
|
||||
dest_rect, draw_framebuffer.handle);
|
||||
}
|
||||
reinterpreter->second->Reinterpret(reinterpret_surface, src_rect, surface, dest_rect);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1465,10 +1239,10 @@ void RasterizerCacheVulkan::FlushRegion(PAddr addr, u32 size, Surface flush_surf
|
||||
|
||||
if (surface->type != SurfaceType::Fill) {
|
||||
SurfaceParams params = surface->FromInterval(interval);
|
||||
surface->DownloadGLTexture(surface->GetSubRect(params), read_framebuffer.handle,
|
||||
draw_framebuffer.handle);
|
||||
surface->DownloadGPUTexture(surface->GetSubRect(params));
|
||||
}
|
||||
surface->FlushGLBuffer(boost::icl::first(interval), boost::icl::last_next(interval));
|
||||
|
||||
surface->FlushGPUBuffer(boost::icl::first(interval), boost::icl::last_next(interval));
|
||||
flushed_intervals += interval;
|
||||
}
|
||||
// Reset dirty regions
|
||||
@@ -1548,11 +1322,8 @@ Surface RasterizerCacheVulkan::CreateSurface(const SurfaceParams& params) {
|
||||
static_cast<SurfaceParams&>(*surface) = params;
|
||||
|
||||
surface->invalid_regions.insert(surface->GetInterval());
|
||||
|
||||
surface->texture =
|
||||
AllocateSurfaceTexture(GetFormatTuple(surface->pixel_format), surface->GetScaledWidth(),
|
||||
surface->GetScaledHeight());
|
||||
|
||||
surface->texture = AllocateSurfaceTexture(GetFormatTuple(surface->pixel_format),
|
||||
surface->GetScaledWidth(), surface->GetScaledHeight());
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ class RasterizerCacheVulkan;
|
||||
class TextureFilterer;
|
||||
class FormatReinterpreterVulkan;
|
||||
|
||||
const vk::Format& GetFormatTuple(SurfaceParams::PixelFormat pixel_format);
|
||||
vk::Format GetFormatTuple(SurfaceParams::PixelFormat pixel_format);
|
||||
|
||||
struct HostTextureTag {
|
||||
vk::Format format;
|
||||
@@ -272,9 +272,6 @@ public:
|
||||
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
||||
Surface GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0);
|
||||
|
||||
/// Get a texture cube based on the texture configuration
|
||||
const CachedTextureCube& GetTextureCube(const TextureCubeConfig& config);
|
||||
|
||||
/// Get the color and depth surfaces based on the framebuffer configuration
|
||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
||||
const Common::Rectangle<s32>& viewport_rect);
|
||||
@@ -346,10 +343,7 @@ private:
|
||||
|
||||
public:
|
||||
VKTexture AllocateSurfaceTexture(vk::Format format, u32 width, u32 height);
|
||||
|
||||
std::unique_ptr<TextureFilterer> texture_filterer;
|
||||
std::unique_ptr<FormatReinterpreterVulkan> format_reinterpreter;
|
||||
std::unique_ptr<TextureDownloader> texture_downloader_es;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
std::unique_ptr<VulkanState> s_vulkan_state{};
|
||||
|
||||
// Define bitwise operators for DirtyFlags enum
|
||||
DirtyFlags operator |=(DirtyFlags lhs, DirtyFlags rhs) {
|
||||
return static_cast<DirtyFlags> (
|
||||
@@ -34,7 +36,7 @@ bool operator <(BindingID lhs, BindingID rhs) {
|
||||
static_cast<u32>(rhs);
|
||||
}
|
||||
|
||||
void VulkanState::Create() {
|
||||
VulkanState::VulkanState() {
|
||||
// Create a dummy texture which can be used in place of a real binding.
|
||||
VKTexture::Info info = {
|
||||
.width = 1,
|
||||
@@ -82,6 +84,17 @@ void VulkanState::Create() {
|
||||
dirty_flags |= DirtyFlags::All;
|
||||
}
|
||||
|
||||
void VulkanState::Create() {
|
||||
if (!s_vulkan_state) {
|
||||
s_vulkan_state = std::make_unique<VulkanState>();
|
||||
}
|
||||
}
|
||||
|
||||
VulkanState& VulkanState::Get() {
|
||||
assert(s_vulkan_state);
|
||||
return *s_vulkan_state;
|
||||
}
|
||||
|
||||
void VulkanState::SetVertexBuffer(VKBuffer* buffer, vk::DeviceSize offset) {
|
||||
if (vertex_buffer == buffer) {
|
||||
return;
|
||||
@@ -148,38 +161,35 @@ void VulkanState::UnbindTexture(u32 index) {
|
||||
dirty_flags |= DirtyFlags::Texture;
|
||||
}
|
||||
|
||||
void VulkanState::PushAttachment(Attachment attachment) {
|
||||
targets.push_back(attachment);
|
||||
}
|
||||
|
||||
void VulkanState::PopAttachment() {
|
||||
if (!targets.empty()) {
|
||||
targets.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanState::BeginRendering() {
|
||||
void VulkanState::BeginRendering(Attachment color, Attachment depth_stencil) {
|
||||
if (rendering) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure attachments are in optimal layout
|
||||
auto& attachment = targets.back();
|
||||
vk::RenderingInfo render_info{{}, attachment.render_area, 1, {}};
|
||||
vk::RenderingInfo render_info{{}, color.image->GetArea(), 1, {}};
|
||||
std::array<vk::RenderingAttachmentInfo, 2> infos{};
|
||||
|
||||
if (attachment.color) {
|
||||
attachment.color->Transition(vk::ImageLayout::eColorAttachmentOptimal);
|
||||
if (color.image) {
|
||||
color.image->Transition(vk::ImageLayout::eColorAttachmentOptimal);
|
||||
|
||||
infos[0] = vk::RenderingAttachmentInfo{
|
||||
color.image->GetView(), color.image->GetLayout(), {}, {}, {},
|
||||
color.load_op, color.store_op, color.clear_color
|
||||
};
|
||||
|
||||
infos[0] = {attachment.color->GetView(), attachment.color->GetLayout()};
|
||||
render_info.colorAttachmentCount = 1;
|
||||
render_info.pColorAttachments = &infos[0];
|
||||
}
|
||||
|
||||
if (attachment.depth_stencil) {
|
||||
attachment.depth_stencil->Transition(vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
if (depth_stencil.image) {
|
||||
depth_stencil.image->Transition(vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
|
||||
infos[1] = vk::RenderingAttachmentInfo{
|
||||
depth_stencil.image->GetView(), depth_stencil.image->GetLayout(), {}, {}, {},
|
||||
depth_stencil.load_op, depth_stencil.store_op, depth_stencil.clear_color
|
||||
};
|
||||
|
||||
infos[1] = {attachment.depth_stencil->GetView(), attachment.depth_stencil->GetLayout()};
|
||||
render_info.pDepthAttachment = &infos[1];
|
||||
render_info.pStencilAttachment = &infos[1];
|
||||
}
|
||||
@@ -375,9 +385,6 @@ void VulkanState::Apply() {
|
||||
// Update resources in descriptor sets if changed
|
||||
UpdateDescriptorSet();
|
||||
|
||||
// Start rendering if not already started
|
||||
BeginRendering();
|
||||
|
||||
// Re-apply dynamic parts of the pipeline
|
||||
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||
if (dirty_flags & DirtyFlags::VertexBuffer) {
|
||||
|
@@ -56,10 +56,10 @@ BindingID operator + (BindingID lhs, u32 rhs) {
|
||||
}
|
||||
|
||||
struct Attachment {
|
||||
VKTexture* color{}, *depth_stencil{};
|
||||
vk::ClearColorValue clear_color;
|
||||
vk::ClearDepthStencilValue depth_color;
|
||||
vk::Rect2D render_area{-1};
|
||||
VKTexture* image{};
|
||||
vk::ClearValue clear_color;
|
||||
vk::AttachmentLoadOp load_op{vk::AttachmentLoadOp::eLoad};
|
||||
vk::AttachmentStoreOp store_op{vk::AttachmentStoreOp::eStore};
|
||||
};
|
||||
|
||||
constexpr u32 DESCRIPTOR_SET_LAYOUT_COUNT = 3;
|
||||
@@ -67,11 +67,12 @@ constexpr u32 DESCRIPTOR_SET_LAYOUT_COUNT = 3;
|
||||
/// Tracks global Vulkan state
|
||||
class VulkanState {
|
||||
public:
|
||||
VulkanState() = default;
|
||||
VulkanState();
|
||||
~VulkanState() = default;
|
||||
|
||||
/// Initialize object to its initial state
|
||||
void Create();
|
||||
static void Create();
|
||||
static VulkanState& Get();
|
||||
|
||||
/// Query state
|
||||
bool DepthTestEnabled() const { return depth_enabled && depth_writes; }
|
||||
@@ -97,10 +98,8 @@ public:
|
||||
vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha);
|
||||
|
||||
/// Rendering
|
||||
void PushAttachment(Attachment attachment);
|
||||
void PopAttachment();
|
||||
void SetFragmentShader(const Pica::Regs& config);
|
||||
void BeginRendering();
|
||||
void BeginRendering(Attachment color, Attachment depth_stencil);
|
||||
void EndRendering();
|
||||
|
||||
/// Configure shader resources
|
||||
@@ -130,7 +129,6 @@ private:
|
||||
bool rendering = false;
|
||||
VKTexture dummy_texture;
|
||||
vk::UniqueSampler sampler;
|
||||
std::vector<Attachment> targets;
|
||||
|
||||
VKBuffer* vertex_buffer{}, * index_buffer{};
|
||||
vk::DeviceSize vertex_offset, index_offset;
|
||||
|
@@ -10,7 +10,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_cache.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@@ -205,22 +204,6 @@ void VKSwapChain::SetupImages() {
|
||||
// Wrap swapchain images with VKTexture
|
||||
swapchain_images[i].image = images[i];
|
||||
swapchain_images[i].image_view = device.createImageViewUnique(color_attachment_view);
|
||||
|
||||
// Create framebuffer for each swapchain image
|
||||
vk::FramebufferCreateInfo framebuffer_info
|
||||
(
|
||||
{},
|
||||
g_vk_res_cache->GetRenderPass(details.format.format,
|
||||
vk::Format::eUndefined,
|
||||
vk::SampleCountFlagBits::e1,
|
||||
vk::AttachmentLoadOp::eLoad),
|
||||
{},
|
||||
details.extent.width,
|
||||
details.extent.height,
|
||||
1
|
||||
);
|
||||
|
||||
swapchain_images[i].framebuffer = device.createFramebufferUnique(framebuffer_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -13,7 +13,6 @@ namespace Vulkan {
|
||||
struct SwapChainImage {
|
||||
vk::Image image;
|
||||
vk::UniqueImageView image_view;
|
||||
vk::UniqueFramebuffer framebuffer;
|
||||
};
|
||||
|
||||
struct SwapChainDetails {
|
||||
@@ -48,10 +47,7 @@ public:
|
||||
vk::SurfaceKHR GetSurface() const { return surface; }
|
||||
vk::SurfaceFormatKHR GetSurfaceFormat() const { return details.format; }
|
||||
vk::SwapchainKHR GetSwapChain() const { return swapchain.get(); }
|
||||
|
||||
/// Retrieve current texture and framebuffer
|
||||
vk::Image GetCurrentImage() { return swapchain_images[image_index].image; }
|
||||
vk::Framebuffer GetCurrentFramebuffer() { return swapchain_images[image_index].framebuffer.get(); }
|
||||
vk::Image GetCurrentImage() const { return swapchain_images[image_index].image; }
|
||||
|
||||
private:
|
||||
void PopulateSwapchainDetails(vk::SurfaceKHR surface, u32 width, u32 height);
|
||||
|
@@ -6,7 +6,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture.h"
|
||||
#include "video_core/renderer_vulkan/vk_task_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_state.h"
|
||||
|
||||
namespace Vulkan {
|
||||
@@ -14,7 +13,8 @@ namespace Vulkan {
|
||||
VKTexture::~VKTexture() {
|
||||
if (texture) {
|
||||
// Make sure to unbind the texture before destroying it
|
||||
g_vk_state->UnbindTexture(this);
|
||||
auto& state = VulkanState::Get();
|
||||
state.UnbindTexture(this);
|
||||
|
||||
auto deleter = [this]() {
|
||||
auto& device = g_vk_instace->GetDevice();
|
||||
@@ -29,6 +29,10 @@ VKTexture::~VKTexture() {
|
||||
}
|
||||
}
|
||||
|
||||
VKTexture& VKTexture::operator=(VKTexture&& move) {
|
||||
|
||||
}
|
||||
|
||||
void VKTexture::Create(const VKTexture::Info& create_info) {
|
||||
auto& device = g_vk_instace->GetDevice();
|
||||
info = create_info;
|
||||
@@ -164,9 +168,9 @@ void VKTexture::Transition(vk::ImageLayout new_layout) {
|
||||
}
|
||||
|
||||
void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> pixels) {
|
||||
auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(pixels.size());
|
||||
auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(pixels.size_bytes());
|
||||
if (!buffer) {
|
||||
LOG_ERROR(Render_Vulkan, "Cannot copy pixels without staging buffer!");
|
||||
LOG_ERROR(Render_Vulkan, "Cannot upload pixels without staging buffer!");
|
||||
}
|
||||
|
||||
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||
@@ -182,13 +186,48 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region,
|
||||
};
|
||||
|
||||
// Transition image to transfer format
|
||||
auto old_layout = GetLayout();
|
||||
Transition(vk::ImageLayout::eTransferDstOptimal);
|
||||
|
||||
command_buffer.copyBufferToImage(g_vk_task_scheduler->GetStaging().GetBuffer(),
|
||||
texture, vk::ImageLayout::eTransferDstOptimal,
|
||||
copy_region);
|
||||
|
||||
// Prepare for shader reads
|
||||
Transition(vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
// Restore layout
|
||||
Transition(old_layout);
|
||||
}
|
||||
|
||||
void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> memory) {
|
||||
auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(memory.size_bytes());
|
||||
if (!buffer) {
|
||||
LOG_ERROR(Render_Vulkan, "Cannot download texture without staging buffer!");
|
||||
}
|
||||
|
||||
auto command_buffer = g_vk_task_scheduler->GetCommandBuffer();
|
||||
|
||||
// Copy pixels to staging buffer
|
||||
vk::BufferImageCopy download_region{
|
||||
offset, row_length, region.extent.height,
|
||||
{info.aspect, level, layer, 1},
|
||||
{region.offset.x, region.offset.y, 0},
|
||||
{region.extent.width, region.extent.height, 1}
|
||||
};
|
||||
|
||||
// Transition image to transfer format
|
||||
auto old_layout = GetLayout();
|
||||
Transition(vk::ImageLayout::eTransferSrcOptimal);
|
||||
|
||||
command_buffer.copyImageToBuffer(texture, vk::ImageLayout::eTransferSrcOptimal,
|
||||
g_vk_task_scheduler->GetStaging().GetBuffer(),
|
||||
download_region);
|
||||
|
||||
// Wait for the data to be available
|
||||
// NOTE: This is really slow and should be reworked
|
||||
g_vk_task_scheduler->Submit(false, true);
|
||||
std::memcpy(memory.data(), buffer, memory.size_bytes());
|
||||
|
||||
// Restore layout
|
||||
Transition(old_layout);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -39,6 +39,9 @@ public:
|
||||
VKTexture() = default;
|
||||
~VKTexture();
|
||||
|
||||
VKTexture(VKTexture&&) = default;
|
||||
VKTexture& operator=(VKTexture&& move) = default;
|
||||
|
||||
/// Create a new Vulkan texture object
|
||||
void Create(const VKTexture::Info& info);
|
||||
|
||||
@@ -50,10 +53,11 @@ public:
|
||||
vk::ImageLayout GetLayout() const { return layout; }
|
||||
u32 GetSamples() const { return info.multisamples; }
|
||||
u32 GetSize() const { return image_size; }
|
||||
vk::Extent2D GetExtent() const { return {info.width, info.height}; }
|
||||
vk::Rect2D GetArea() const { return {{0, 0},{info.width, info.height}}; }
|
||||
|
||||
/// Copies CPU side pixel data to the GPU texture buffer
|
||||
void Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> pixels);
|
||||
void Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> dst);
|
||||
|
||||
/// Used to transition the image to an optimal layout during transfers
|
||||
void Transition(vk::ImageLayout new_layout);
|
||||
|
Reference in New Issue
Block a user