video_core: Make renderer common

* Also rename to DisplayRenderer
This commit is contained in:
emufan4568
2022-08-10 15:36:21 +03:00
parent 67c20e04b4
commit c5f08a1408
12 changed files with 701 additions and 36 deletions

View File

@ -35,6 +35,8 @@ add_library(video_core STATIC
common/rasterizer.h
common/rasterizer_cache.cpp
common/rasterizer_cache.h
common/renderer.cpp
common/renderer.h
common/shader_runtime_cache.h
common/shader_disk_cache.cpp
common/shader_disk_cache.h

View File

@ -14,7 +14,10 @@ class EmuWindow;
namespace VideoCore {
class ShaderDiskCache;
// A piece of information the video frontend can query the backend about
enum class Query {
PresentFormat = 0
};
// Common interface of a video backend
class BackendBase {
@ -22,8 +25,17 @@ public:
BackendBase(Frontend::EmuWindow& window) : window(window) {}
virtual ~BackendBase() = default;
// Acquires the next swapchain images and begins rendering
virtual bool BeginPresent() = 0;
// Triggers a swapchain buffer swap
virtual void SwapBuffers();
virtual void EndPresent() = 0;
// Returns the framebuffer created from the swapchain images
virtual FramebufferHandle GetWindowFramebuffer() = 0;
// Asks the driver about a particular piece of information
virtual u64 QueryDriver(Query query) = 0;
// Creates a backend specific texture handle
virtual TextureHandle CreateTexture(TextureInfo info) = 0;
@ -60,7 +72,7 @@ public:
// Executes a compute shader
virtual void DispatchCompute(PipelineHandle pipeline, Common::Vec3<u32> groupsize,
Common::Vec3<u32> groups) = 0;
private:
protected:
Frontend::EmuWindow& window;
};

View File

@ -39,6 +39,8 @@ enum class BindingType : u32 {
using BindingGroup = BitFieldArray<0, 3, MAX_BINDINGS_IN_GROUP, BindingType>;
static_assert(sizeof(BindingGroup));
/**
* Describes all the resources used in the pipeline
*/
@ -48,6 +50,8 @@ struct PipelineLayoutInfo {
u8 push_constant_block_size = 0;
};
static_assert(sizeof(PipelineLayoutInfo));
/**
* The pipeline state is tightly packed with bitfields to reduce
* the overhead of hashing as much as possible
@ -83,7 +87,8 @@ union BlendState {
BitField<16, 4, Pica::BlendFactor> dst_alpha_blend_factor;
BitField<20, 3, Pica::BlendEquation> alpha_blend_eq;
BitField<23, 4, u32> color_write_mask;
BitField<27, 4, Pica::LogicOp> logic_op;
BitField<27, 1, u32> logic_op_enable;
BitField<28, 4, Pica::LogicOp> logic_op;
};
enum class AttribType : u32 {
@ -159,11 +164,14 @@ public:
// Binds the sampler in the specified slot
virtual void BindSampler(u32 group, u32 slot, SamplerHandle handle) = 0;
// Binds a small uniform block (under 256 bytes) to the current pipeline
virtual void BindPushConstant(std::span<const std::byte> data) = 0;
// Sets the viewport of the pipeline
virtual void SetViewport(Rect2D viewport) = 0;
virtual void SetViewport(float x, float y, float width, float height) = 0;
// Sets the scissor of the pipeline
virtual void SetScissor(Rect2D scissor) = 0;
virtual void SetScissor(s32 x, s32 y, u32 width, u32 height) = 0;
// Returns the pipeline type (Graphics or Compute)
PipelineType GetType() const {
@ -171,7 +179,7 @@ public:
}
protected:
PipelineInfo info;
const PipelineInfo info;
PipelineType type = PipelineType::Graphics;
};

View File

@ -9,6 +9,7 @@
#include "core/hw/gpu.h"
#include "video_core/pica_state.h"
#include "video_core/common/rasterizer.h"
#include "video_core/common/renderer.h"
#include "video_core/common/pipeline_cache.h"
#include "video_core/video_core.h"
@ -78,7 +79,7 @@ constexpr VertexLayout HardwareVertex::GetVertexLayout() {
constexpr std::array sizes = {4, 4, 2, 2, 2, 1, 4, 3};
u32 offset = 0;
for (u32 loc = 0; loc < layout.attribute_count; loc++) {
for (u32 loc = 0; loc < 8; loc++) {
VertexAttribute& attribute = layout.attributes[loc];
attribute.binding.Assign(0);
attribute.location.Assign(loc);
@ -1559,7 +1560,7 @@ bool Rasterizer::AccelerateDisplay(const GPU::Regs::FramebufferConfig& config,
(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.handle;
screen_info.display_texture = src_surface->texture;
return true;
}

View File

@ -38,6 +38,7 @@ struct HardwareVertex {
};
class BackendBase;
struct ScreenInfo;
class Rasterizer {
public:

View File

@ -241,9 +241,11 @@ bool RasterizerCache::FillSurface(const Surface& surface, const u8* fill_data, C
.depth_stencil = depth_surface ? surface->texture : TextureHandle{}
};
// Some backends (Vulkan) provide texture clear functions but in general
// it's still more efficient to use framebuffers for fills to take advantage
// of the dedicated clear engine on the GPU
/**
* Some backends (for example Vulkan) provide texture clear functions but in general
* it's still more efficient to use framebuffers for fills to take advantage of the dedicated
* clear engine on the GPU
*/
FramebufferHandle framebuffer;
if (auto iter = framebuffer_cache.find(framebuffer_info); iter != framebuffer_cache.end()) {
framebuffer = iter->second;
@ -258,7 +260,7 @@ bool RasterizerCache::FillSurface(const Surface& surface, const u8* fill_data, C
if (surface->type == SurfaceType::Color || surface->type == SurfaceType::Texture) {
Pica::Texture::TextureInfo tex_info{};
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(surface->pixel_format);
Common::Vec4f color_values = Pica::Texture::LookupTexture(fill_data, 0, 0, tex_info) / 255.f;
const auto color_values = Pica::Texture::LookupTexture(fill_data, 0, 0, tex_info) / 255.f;
framebuffer->DoClear(color_values, 0.0f, 0);
} else if (surface->type == SurfaceType::Depth) {
@ -286,6 +288,7 @@ bool RasterizerCache::FillSurface(const Surface& surface, const u8* fill_data, C
framebuffer->DoClear({}, depth_float, stencil_int);
}
return true;
}

View File

@ -0,0 +1,508 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <glm/gtc/matrix_transform.hpp>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/frontend/emu_window.h"
#include "core/frontend/framebuffer_layout.h"
#include "core/hw/gpu.h"
#include "core/hw/hw.h"
#include "core/hw/lcd.h"
#include "core/settings.h"
#include "video_core/common/renderer.h"
#include "video_core/common/rasterizer.h"
#include "video_core/renderer_vulkan/vk_backend.h"
#include "video_core/video_core.h"
namespace VideoCore {
static std::string vertex_shader_source = R"(
#version 450 core
layout (location = 0) in vec2 vert_position;
layout (location = 1) in vec2 vert_tex_coord;
layout (location = 0) out vec2 frag_tex_coord;
layout (std140, push_constant) uniform PresentUniformData {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id;
int layer;
int reverse_interlaced;
};
void main() {
vec4 position = vec4(vert_position, 0.0, 1.0) * modelview_matrix;
gl_Position = vec4(position.x, -position.y, 0.0, 1.0);
frag_tex_coord = vert_tex_coord;
}
)";
static std::string fragment_shader_source = R"(
layout (location = 0) in vec2 frag_tex_coord;
layout (location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform texture2D top_screen;
layout (std140, push_constant) uniform PresentUniformData {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int screen_id;
int layer;
int reverse_interlaced;
};
void main() {
color = texture(top_screen, frag_tex_coord);
}
)";
static std::string fragment_shader_anaglyph_source = 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);
layout (location = 0) in vec2 frag_tex_coord;
layout (location = 0) out vec4 color;
layout (set = 0, binding = 0) uniform sampler2D top_screen;
layout (set = 0, binding = 1) uniform sampler2D top_screen_r;
void main() {
vec4 color_tex_l = texture(top_screen, frag_tex_coord);
vec4 color_tex_r = texture(top_screen_r, frag_tex_coord);
color = vec4(color_tex_l.rgb * l + color_tex_r.rgb * r, color_tex_l.a);
}
)";
static std::string fragment_shader_interlaced_source = R"(
layout (location = 0) in vec2 frag_tex_coord;
layout (location = 0) out vec4 color;
layout (std140, push_constant) uniform PresentUniformData {
mat4 modelview_matrix;
vec4 i_resolution;
vec4 o_resolution;
int layer;
int reverse_interlaced;
};
layout (set = 0, binding = 0) uniform sampler2D top_screen;
layout (set = 0, binding = 1) uniform sampler2D top_screen_r;
void main() {
float screen_row = o_resolution.x * frag_tex_coord.x;
if (int(screen_row) % 2 == reverse_interlaced) {
color = texture(top_screen, frag_tex_coord);
} else {
color = texture(top_screen_r, frag_tex_coord);
}
}
)";
constexpr VertexLayout ScreenRectVertex::GetVertexLayout() {
VertexLayout layout{};
layout.attribute_count = 2;
layout.binding_count = 1;
// Define binding
layout.bindings[0].binding.Assign(0);
layout.bindings[0].fixed.Assign(0);
layout.bindings[0].stride.Assign(sizeof(ScreenRectVertex));
// Define the attributes
for (u32 loc = 0; loc < 2; loc++) {
layout.attributes[loc].binding.Assign(0);
layout.attributes[loc].location.Assign(loc);
layout.attributes[loc].offset.Assign(loc * sizeof(glm::vec2));
layout.attributes[loc].size.Assign(2);
layout.attributes[loc].type.Assign(AttribType::Float);
}
return layout;
}
// Renderer pipeline layout
static constexpr PipelineLayoutInfo RENDERER_PIPELINE_INFO = {
.group_count = 2,
.binding_groups = {
BindingGroup{
BindingType::Texture, // Top screen
BindingType::Texture // Top screen stereo pair
},
BindingGroup{
BindingType::Sampler
}
},
.push_constant_block_size = sizeof(PresentUniformData)
};
DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(window) {
//window.mailbox = nullptr;
backend = std::make_unique<Vulkan::Backend>(window);
rasterizer = std::make_unique<VideoCore::Rasterizer>(window, backend);
// Create vertex buffer for the screen rectangle
const BufferInfo vertex_info = {
.capacity = sizeof(ScreenRectVertex) * 10,
.usage = BufferUsage::Vertex
};
vertex_buffer = backend->CreateBuffer(vertex_info);
const std::array fragment_shaders = {&fragment_shader_source,
&fragment_shader_anaglyph_source,
&fragment_shader_interlaced_source};
const auto color_format = static_cast<TextureFormat>(backend->QueryDriver(Query::PresentFormat));
PipelineInfo present_pipeline_info = {
.vertex_layout = ScreenRectVertex::GetVertexLayout(),
.layout = RENDERER_PIPELINE_INFO,
.color_attachment = color_format,
.depth_attachment = TextureFormat::Undefined
};
// Set topology to strip
present_pipeline_info.rasterization.topology.Assign(Pica::TriangleTopology::Strip);
// Create vertex and fragment shaders
vertex_shader = backend->CreateShader(ShaderStage::Vertex, "Present vertex shader",
vertex_shader_source);
for (int i = 0; i < PRESENT_PIPELINES; i++) {
const std::string name = fmt::format("Present shader {:d}", i);
present_shaders[i] = backend->CreateShader(ShaderStage::Fragment, name, *fragment_shaders[i]);
// Create associated pipeline
present_pipeline_info.shaders[0] = vertex_shader;
present_pipeline_info.shaders[1] = present_shaders[i];
present_pipelines[i] = backend->CreatePipeline(PipelineType::Graphics, present_pipeline_info);
}
}
void DisplayRenderer::PrepareRendertarget() {
for (int i = 0; i < 3; i++) {
int fb_id = i == 2 ? 1 : 0;
const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id];
// Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04
u32 lcd_color_addr = (fb_id == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom);
lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr;
LCD::Regs::ColorFill color_fill = {0};
LCD::Read(color_fill.raw, lcd_color_addr);
if (color_fill.is_enabled) {
LoadColorToActiveTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, screen_infos[i]);
} else {
const TextureHandle& texture = screen_infos[i].texture;
u32 fwidth = framebuffer.width;
u32 fheight = framebuffer.height;
if (texture->GetWidth() != fwidth || texture->GetHeight() != fheight ||
screen_infos[i].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
// performance problem.
ConfigureFramebufferTexture(screen_infos[i], framebuffer);
}
LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1);
}
}
}
void DisplayRenderer::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer,
ScreenInfo& screen_info, bool right_eye) {
if (framebuffer.address_right1 == 0 || framebuffer.address_right2 == 0) {
right_eye = false;
}
const PAddr framebuffer_addr = framebuffer.active_fb == 0
? (!right_eye ? framebuffer.address_left1 : framebuffer.address_right1)
: (!right_eye ? framebuffer.address_left2 : framebuffer.address_right2);
LOG_TRACE(Render_Vulkan, "0x{:08x} bytes from 0x{:08x}({}x{}), fmt {:x}",
framebuffer.stride * framebuffer.height, framebuffer_addr, framebuffer.width.Value(),
framebuffer.height.Value(), framebuffer.format);
int bpp = GPU::Regs::BytesPerPixel(framebuffer.color_format);
std::size_t pixel_stride = framebuffer.stride / bpp;
// OpenGL only supports specifying a stride in units of pixels, not bytes, unfortunately
ASSERT(pixel_stride * bpp == framebuffer.stride);
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT, which by default
// only allows rows to have a memory alignement of 4.
ASSERT(pixel_stride % 4 == 0);
if (!rasterizer->AccelerateDisplay(framebuffer, framebuffer_addr, static_cast<u32>(pixel_stride), screen_info)) {
ASSERT(false);
// Reset the screen info's display texture to its own permanent texture
screen_info.display_texture = screen_info.texture;
screen_info.display_texcoords = Common::Rectangle<f32>{0.f, 0.f, 1.f, 1.f};
rasterizer->FlushRegion(framebuffer_addr, framebuffer.stride * framebuffer.height);
//const u8* data_ptr = VideoCore::g_memory->GetPhysicalPointer(framebuffer_addr);
//const u32 data_size = screen_info.texture->GetWidth() * screen_info.texture->GetHeight() *
//auto framebuffer_data = std::span<const u8>{data_ptr, screen_info.texture.GetSize()};
//Rect2D region{0, 0, framebuffer.width, framebuffer.height};
//screen_info.texture->Upload(region, pixel_stride, framebuffer_data);
}
}
void DisplayRenderer::LoadColorToActiveTexture(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);
u8 framebuffer_data[3] = {color_r, color_g, color_b};
// Update existing texture
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();*/
}
void DisplayRenderer::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::Regs::FramebufferConfig& framebuffer) {
screen.format = framebuffer.color_format;
auto ToTextureFormat = [&screen]() {
switch (screen.format) {
case GPU::Regs::PixelFormat::RGBA8:
return TextureFormat::RGBA8;
case GPU::Regs::PixelFormat::RGB8:
return TextureFormat::RGB8;
case GPU::Regs::PixelFormat::RGB565:
return TextureFormat::RGB565;
case GPU::Regs::PixelFormat::RGB5A1:
return TextureFormat::RGB5A1;
case GPU::Regs::PixelFormat::RGBA4:
return TextureFormat::RGBA4;
default:
UNIMPLEMENTED();
}
};
const TextureInfo texture_info = {
.width = static_cast<u16>(framebuffer.width),
.height = static_cast<u16>(framebuffer.height),
.levels = 1,
.type = TextureType::Texture2D,
.view_type = TextureViewType::View2D,
.format = ToTextureFormat()
};
screen.texture = backend->CreateTexture(texture_info);
}
void DisplayRenderer::ReloadPresentPipeline() {
const auto& render_3d = Settings::values.render_3d;
// Update current pipeline
switch (render_3d) {
case Settings::StereoRenderOption::Anaglyph:
current_pipeline = present_pipelines[1];
break;
case Settings::StereoRenderOption::ReverseInterlaced:
case Settings::StereoRenderOption::Interlaced:
current_pipeline = present_pipelines[2];
break;
default:
current_pipeline = present_pipelines[0];
}
// Update uniform data
uniform_data.reverse_interlaced = (render_3d == Settings::StereoRenderOption::ReverseInterlaced);
}
void DisplayRenderer::DrawSingleScreen(u32 screen, bool rotate, float x, float y, float w, float h) {
const ScreenInfo& screen_info = screen_infos[screen];
const auto& texcoords = screen_info.display_texcoords;
// Clear the swapchain framebuffer
FramebufferHandle display = backend->GetWindowFramebuffer();
display->DoClear(clear_color, 0.f, 0);
// Update viewport and scissor
const auto& color_surface = display->GetColorAttachment();
current_pipeline->SetViewport(0.f, 0.f, color_surface->GetWidth(), color_surface->GetHeight());
current_pipeline->SetScissor(0, 0, color_surface->GetWidth(), color_surface->GetHeight());
std::array<ScreenRectVertex, 4> vertices;
if (rotate) {
vertices = {
ScreenRectVertex{x, y, texcoords.bottom, texcoords.left},
ScreenRectVertex{x + w, y, texcoords.bottom, texcoords.right},
ScreenRectVertex{x, y + h, texcoords.top, texcoords.left},
ScreenRectVertex{x + w, y + h, texcoords.top, texcoords.right}
};
} else {
vertices = {
ScreenRectVertex{x, y, texcoords.bottom, texcoords.right},
ScreenRectVertex{x + w, y, texcoords.top, texcoords.right},
ScreenRectVertex{x, y + h, texcoords.bottom, texcoords.left},
ScreenRectVertex{x + w, y + h, texcoords.top, texcoords.left}
};
}
const u32 size = sizeof(ScreenRectVertex) * vertices.size();
const u32 mapped_offset = vertex_buffer->GetCurrentOffset();
auto vertex_data = vertex_buffer->Map(size);
// Copy vertex data
std::memcpy(vertex_data.data(), vertices.data(), size);
vertex_buffer->Commit(size);
// 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();
const u32 width = screen_info.texture->GetWidth();
const u32 height = screen_info.texture->GetHeight();
uniform_data.i_resolution = glm::vec4{width * scale_factor, height * scale_factor,
1.0f / (width * scale_factor),
1.0f / (height * scale_factor)};
uniform_data.o_resolution = glm::vec4{h, w, 1.0f / h, 1.0f / w};
// Upload uniform data
current_pipeline->BindPushConstant(uniform_data.AsBytes());
// Bind the vertex buffer and draw
const std::array offsets = {mapped_offset};
backend->BindVertexBuffer(vertex_buffer, offsets);
backend->Draw(current_pipeline, FramebufferHandle{}, 0, vertices.size());
}
void DisplayRenderer::DrawScreens(bool flipped) {
const auto& layout = render_window.GetFramebufferLayout();
if (VideoCore::g_renderer_bg_color_update_requested.exchange(false)) {
// Update background color before drawing
clear_color = Common::Vec4f{Settings::values.bg_red, Settings::values.bg_green,
Settings::values.bg_blue, 0.0f};
}
// Set the new filtering mode for the sampler
if (VideoCore::g_renderer_sampler_update_requested.exchange(false)) {
ReloadSampler();
}
// Update present pipeline before drawing
if (VideoCore::g_renderer_shader_update_requested.exchange(false)) {
ReloadPresentPipeline();
}
const auto& top_screen = layout.top_screen;
//const auto& bottom_screen = layout.bottom_screen;
// Set projection matrix
uniform_data.modelview = glm::transpose(glm::ortho<float>(0.f, layout.width, layout.height, 0.0f, 0.f, 1.f));
uniform_data.layer = 0;
if (layout.top_screen_enabled) {
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
DrawSingleScreen(0, layout.is_rotated, top_screen.left, top_screen.top,
top_screen.GetWidth(), top_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
DrawSingleScreen(0, layout.is_rotated, top_screen.left / 2.f, top_screen.top,
top_screen.GetWidth() / 2.f, top_screen.GetHeight());
uniform_data.layer = 1;
DrawSingleScreen(1, layout.is_rotated, (top_screen.left / 2.f) + (layout.width / 2.f), top_screen.top,
top_screen.GetWidth() / 2.f, top_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
DrawSingleScreen(0, layout.is_rotated, layout.top_screen.left, layout.top_screen.top,
layout.top_screen.GetWidth(), layout.top_screen.GetHeight());
uniform_data.layer = 1;
DrawSingleScreen(1, layout.is_rotated, layout.cardboard.top_screen_right_eye + (layout.width / 2.f),
layout.top_screen.top, layout.top_screen.GetWidth(), layout.top_screen.GetHeight());
}
}
uniform_data.layer = 0;
/*if (layout.bottom_screen_enabled) {
if (layout.is_rotated) {
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
DrawSingleScreenRotated(2, (float)bottom_screen.left,
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
(float)bottom_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
DrawSingleScreenRotated(
2, (float)bottom_screen.left / 2, (float)bottom_screen.top,
(float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight());
uniform_data.layer = 1;
DrawSingleScreenRotated(
2, ((float)bottom_screen.left / 2) + ((float)layout.width / 2),
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
(float)bottom_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
DrawSingleScreenRotated(2, layout.bottom_screen.left,
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
layout.bottom_screen.GetHeight());
uniform_data.layer = 1;
DrawSingleScreenRotated(2,
layout.cardboard.bottom_screen_right_eye +
((float)layout.width / 2),
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
layout.bottom_screen.GetHeight());
}
} else {
if (Settings::values.render_3d == Settings::StereoRenderOption::Off) {
DrawSingleScreen(2, (float)bottom_screen.left,
(float)bottom_screen.top, (float)bottom_screen.GetWidth(),
(float)bottom_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) {
DrawSingleScreen(2, (float)bottom_screen.left / 2,
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
(float)bottom_screen.GetHeight());
uniform_data.layer = 1;
DrawSingleScreen(2,
((float)bottom_screen.left / 2) + ((float)layout.width / 2),
(float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2,
(float)bottom_screen.GetHeight());
} else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) {
DrawSingleScreen(2, layout.bottom_screen.left,
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
layout.bottom_screen.GetHeight());
uniform_data.layer = 1;
DrawSingleScreen(2,
layout.cardboard.bottom_screen_right_eye +
((float)layout.width / 2),
layout.bottom_screen.top, layout.bottom_screen.GetWidth(),
layout.bottom_screen.GetHeight());
}
}
}*/
}
void DisplayRenderer::SwapBuffers() {
// Configure current framebuffer and recreate swapchain if necessary
PrepareRendertarget();
// Present the 3DS screens
if (backend->BeginPresent()) {
DrawScreens(false);
backend->EndPresent();
}
}
void DisplayRenderer::UpdateCurrentFramebufferLayout(bool is_portrait_mode) {
const Layout::FramebufferLayout& layout = render_window.GetFramebufferLayout();
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height, is_portrait_mode);
}
} // namespace Vulkan

View File

@ -0,0 +1,143 @@
// Copyright 2022 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <glm/glm.hpp>
#include "common/math_util.h"
#include "core/hw/gpu.h"
#include "video_core/common/pipeline.h"
namespace Frontend {
class EmuWindow;
}
namespace Layout {
struct FramebufferLayout;
}
namespace VideoCore {
class BackendBase;
class Rasterizer;
// Structure used for storing information about the display target for each 3DS screen
struct ScreenInfo {
TextureHandle display_texture;
TextureHandle texture;
SamplerHandle sampler;
Common::Rectangle<f32> display_texcoords;
GPU::Regs::PixelFormat format;
};
// Uniform data used for presenting the 3DS screens
struct PresentUniformData {
glm::mat4 modelview;
glm::vec4 i_resolution;
glm::vec4 o_resolution;
int screen_id = 0;
int layer = 0;
int reverse_interlaced = 0;
// Returns an immutable byte view of the uniform data
auto AsBytes() const {
return std::as_bytes(std::span{this, 1});
}
};
static_assert(sizeof(PresentUniformData) < 256, "PresentUniformData must be below 256 bytes!");
// Vertex structure that the drawn screen rectangles are composed of.
struct ScreenRectVertex {
ScreenRectVertex() = default;
ScreenRectVertex(float x, float y, float u, float v) :
position(x, y), tex_coord(u, v) {}
// Returns the pipeline vertex layout of the vertex
constexpr static VertexLayout GetVertexLayout();
glm::vec2 position;
glm::vec2 tex_coord;
};
constexpr u32 PRESENT_PIPELINES = 3;
class DisplayRenderer {
public:
DisplayRenderer(Frontend::EmuWindow& window);
~DisplayRenderer();
void SwapBuffers();
void TryPresent(int timeout_ms) {}
float GetCurrentFPS() const {
return m_current_fps;
}
int GetCurrentFrame() const {
return m_current_frame;
}
Rasterizer* Rasterizer() const {
return rasterizer.get();
}
Frontend::EmuWindow& GetRenderWindow() {
return render_window;
}
const Frontend::EmuWindow& GetRenderWindow() const {
return render_window;
}
void Sync();
private:
void PrepareRendertarget();
void ConfigureFramebufferTexture(ScreenInfo& screen, const GPU::Regs::FramebufferConfig& framebuffer);
// Updates display pipeline according to the shader configuration
void ReloadPresentPipeline();
// Updates the sampler used for special effects
void ReloadSampler() {}
// Draws the emulated screens to the emulator window.
void DrawScreens(bool flipped);
// Draws a single texture to the emulator window, optionally rotating
// the texture to correct for the 3DS's LCD rotation.
void DrawSingleScreen(u32 screen, bool rotated, float x, float y, float w, float h);
// Loads framebuffer from emulated memory into the display information structure
void LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer,
ScreenInfo& screen_info, bool right_eye);
// Fills active OpenGL texture with the given RGB color.
void LoadColorToActiveTexture(u8 color_r, u8 color_g, u8 color_b, const ScreenInfo& screen);
// Updates the framebuffer layout of the contained render window handle.
void UpdateCurrentFramebufferLayout(bool is_portrait_mode = {});
private:
std::unique_ptr<VideoCore::Rasterizer> rasterizer;
std::unique_ptr<BackendBase> backend;
Frontend::EmuWindow& render_window;
Common::Vec4f clear_color;
f32 m_current_fps = 0.0f;
int m_current_frame = 0;
// Present pipelines (Normal, Anaglyph, Interlaced)
std::array<PipelineHandle, PRESENT_PIPELINES> present_pipelines;
std::array<ShaderHandle, PRESENT_PIPELINES> present_shaders;
PipelineHandle current_pipeline;
ShaderHandle vertex_shader;
// Display information for top and bottom screens respectively
std::array<ScreenInfo, 3> screen_infos;
PresentUniformData uniform_data;
BufferHandle vertex_buffer;
};
} // namespace VideoCore

View File

@ -312,11 +312,12 @@ uniform int reverse_interlaced;
void main() {
float screen_row = o_resolution.x * frag_tex_coord.x;
if (int(screen_row) % 2 == reverse_interlaced)
if (int(screen_row) % 2 == reverse_interlaced) {
color = texture(color_texture, frag_tex_coord);
else
} else {
color = texture(color_texture_r, frag_tex_coord);
}
}
)";
/**

View File

@ -18,7 +18,7 @@ class Texture;
constexpr u32 RENDERPASS_COUNT = (MAX_COLOR_FORMATS + 1) * (MAX_DEPTH_FORMATS + 1);
constexpr u32 DESCRIPTOR_BANK_SIZE = 64;
class Backend : public VideoCore::BackendBase {
class Backend final : public VideoCore::BackendBase {
public:
Backend(Frontend::EmuWindow& window);
~Backend();

View File

@ -8,10 +8,7 @@
#include "core/settings.h"
#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_vars.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/common/renderer.h"
#include "video_core/video_core.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -19,7 +16,7 @@
namespace VideoCore {
std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
std::unique_ptr<DisplayRenderer> g_renderer; ///< Renderer plugin
std::atomic<bool> g_hw_renderer_enabled;
std::atomic<bool> g_shader_jit_enabled;
@ -44,25 +41,14 @@ ResultStatus Init(Frontend::EmuWindow& emu_window, Memory::MemorySystem& memory)
g_memory = &memory;
Pica::Init();
OpenGL::GLES = Settings::values.use_gles;
g_renderer = std::make_unique<Vulkan::RendererVulkan>(emu_window);
ResultStatus result = g_renderer->Init();
if (result != ResultStatus::Success) {
LOG_ERROR(Render, "initialization failed !");
} else {
LOG_DEBUG(Render, "initialized OK");
}
return result;
g_renderer = std::make_unique<DisplayRenderer>(emu_window);
return ResultStatus::Success;
}
/// Shutdown the video core
void Shutdown() {
Pica::Shutdown();
g_renderer->ShutDown();
g_renderer.reset();
LOG_DEBUG(Render, "shutdown OK");

View File

@ -23,8 +23,8 @@ class MemorySystem;
namespace VideoCore {
class RendererBase;
extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
class DisplayRenderer;
extern std::unique_ptr<DisplayRenderer> g_renderer; ///< Renderer plugin
// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
// qt ui)