video_core: Make renderer common
* Also rename to DisplayRenderer
This commit is contained in:
@ -35,6 +35,8 @@ add_library(video_core STATIC
|
|||||||
common/rasterizer.h
|
common/rasterizer.h
|
||||||
common/rasterizer_cache.cpp
|
common/rasterizer_cache.cpp
|
||||||
common/rasterizer_cache.h
|
common/rasterizer_cache.h
|
||||||
|
common/renderer.cpp
|
||||||
|
common/renderer.h
|
||||||
common/shader_runtime_cache.h
|
common/shader_runtime_cache.h
|
||||||
common/shader_disk_cache.cpp
|
common/shader_disk_cache.cpp
|
||||||
common/shader_disk_cache.h
|
common/shader_disk_cache.h
|
||||||
|
@ -14,7 +14,10 @@ class EmuWindow;
|
|||||||
|
|
||||||
namespace VideoCore {
|
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
|
// Common interface of a video backend
|
||||||
class BackendBase {
|
class BackendBase {
|
||||||
@ -22,8 +25,17 @@ public:
|
|||||||
BackendBase(Frontend::EmuWindow& window) : window(window) {}
|
BackendBase(Frontend::EmuWindow& window) : window(window) {}
|
||||||
virtual ~BackendBase() = default;
|
virtual ~BackendBase() = default;
|
||||||
|
|
||||||
|
// Acquires the next swapchain images and begins rendering
|
||||||
|
virtual bool BeginPresent() = 0;
|
||||||
|
|
||||||
// Triggers a swapchain buffer swap
|
// 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
|
// Creates a backend specific texture handle
|
||||||
virtual TextureHandle CreateTexture(TextureInfo info) = 0;
|
virtual TextureHandle CreateTexture(TextureInfo info) = 0;
|
||||||
@ -60,7 +72,7 @@ public:
|
|||||||
// Executes a compute shader
|
// Executes a compute shader
|
||||||
virtual void DispatchCompute(PipelineHandle pipeline, Common::Vec3<u32> groupsize,
|
virtual void DispatchCompute(PipelineHandle pipeline, Common::Vec3<u32> groupsize,
|
||||||
Common::Vec3<u32> groups) = 0;
|
Common::Vec3<u32> groups) = 0;
|
||||||
private:
|
protected:
|
||||||
Frontend::EmuWindow& window;
|
Frontend::EmuWindow& window;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,6 +39,8 @@ enum class BindingType : u32 {
|
|||||||
|
|
||||||
using BindingGroup = BitFieldArray<0, 3, MAX_BINDINGS_IN_GROUP, BindingType>;
|
using BindingGroup = BitFieldArray<0, 3, MAX_BINDINGS_IN_GROUP, BindingType>;
|
||||||
|
|
||||||
|
static_assert(sizeof(BindingGroup));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes all the resources used in the pipeline
|
* Describes all the resources used in the pipeline
|
||||||
*/
|
*/
|
||||||
@ -48,6 +50,8 @@ struct PipelineLayoutInfo {
|
|||||||
u8 push_constant_block_size = 0;
|
u8 push_constant_block_size = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(PipelineLayoutInfo));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pipeline state is tightly packed with bitfields to reduce
|
* The pipeline state is tightly packed with bitfields to reduce
|
||||||
* the overhead of hashing as much as possible
|
* the overhead of hashing as much as possible
|
||||||
@ -83,7 +87,8 @@ union BlendState {
|
|||||||
BitField<16, 4, Pica::BlendFactor> dst_alpha_blend_factor;
|
BitField<16, 4, Pica::BlendFactor> dst_alpha_blend_factor;
|
||||||
BitField<20, 3, Pica::BlendEquation> alpha_blend_eq;
|
BitField<20, 3, Pica::BlendEquation> alpha_blend_eq;
|
||||||
BitField<23, 4, u32> color_write_mask;
|
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 {
|
enum class AttribType : u32 {
|
||||||
@ -159,11 +164,14 @@ public:
|
|||||||
// Binds the sampler in the specified slot
|
// Binds the sampler in the specified slot
|
||||||
virtual void BindSampler(u32 group, u32 slot, SamplerHandle handle) = 0;
|
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
|
// 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
|
// 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)
|
// Returns the pipeline type (Graphics or Compute)
|
||||||
PipelineType GetType() const {
|
PipelineType GetType() const {
|
||||||
@ -171,7 +179,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PipelineInfo info;
|
const PipelineInfo info;
|
||||||
PipelineType type = PipelineType::Graphics;
|
PipelineType type = PipelineType::Graphics;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "core/hw/gpu.h"
|
#include "core/hw/gpu.h"
|
||||||
#include "video_core/pica_state.h"
|
#include "video_core/pica_state.h"
|
||||||
#include "video_core/common/rasterizer.h"
|
#include "video_core/common/rasterizer.h"
|
||||||
|
#include "video_core/common/renderer.h"
|
||||||
#include "video_core/common/pipeline_cache.h"
|
#include "video_core/common/pipeline_cache.h"
|
||||||
#include "video_core/video_core.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};
|
constexpr std::array sizes = {4, 4, 2, 2, 2, 1, 4, 3};
|
||||||
u32 offset = 0;
|
u32 offset = 0;
|
||||||
|
|
||||||
for (u32 loc = 0; loc < layout.attribute_count; loc++) {
|
for (u32 loc = 0; loc < 8; loc++) {
|
||||||
VertexAttribute& attribute = layout.attributes[loc];
|
VertexAttribute& attribute = layout.attributes[loc];
|
||||||
attribute.binding.Assign(0);
|
attribute.binding.Assign(0);
|
||||||
attribute.location.Assign(loc);
|
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.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);
|
(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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ struct HardwareVertex {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class BackendBase;
|
class BackendBase;
|
||||||
|
struct ScreenInfo;
|
||||||
|
|
||||||
class Rasterizer {
|
class Rasterizer {
|
||||||
public:
|
public:
|
||||||
|
@ -241,9 +241,11 @@ bool RasterizerCache::FillSurface(const Surface& surface, const u8* fill_data, C
|
|||||||
.depth_stencil = depth_surface ? surface->texture : TextureHandle{}
|
.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
|
* Some backends (for example Vulkan) provide texture clear functions but in general
|
||||||
// of the dedicated clear engine on the GPU
|
* it's still more efficient to use framebuffers for fills to take advantage of the dedicated
|
||||||
|
* clear engine on the GPU
|
||||||
|
*/
|
||||||
FramebufferHandle framebuffer;
|
FramebufferHandle framebuffer;
|
||||||
if (auto iter = framebuffer_cache.find(framebuffer_info); iter != framebuffer_cache.end()) {
|
if (auto iter = framebuffer_cache.find(framebuffer_info); iter != framebuffer_cache.end()) {
|
||||||
framebuffer = iter->second;
|
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) {
|
if (surface->type == SurfaceType::Color || surface->type == SurfaceType::Texture) {
|
||||||
Pica::Texture::TextureInfo tex_info{};
|
Pica::Texture::TextureInfo tex_info{};
|
||||||
tex_info.format = static_cast<Pica::TexturingRegs::TextureFormat>(surface->pixel_format);
|
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);
|
framebuffer->DoClear(color_values, 0.0f, 0);
|
||||||
} else if (surface->type == SurfaceType::Depth) {
|
} 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);
|
framebuffer->DoClear({}, depth_float, stencil_int);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
508
src/video_core/common/renderer.cpp
Normal file
508
src/video_core/common/renderer.cpp
Normal 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
|
143
src/video_core/common/renderer.h
Normal file
143
src/video_core/common/renderer.h
Normal 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
|
@ -312,10 +312,11 @@ uniform int reverse_interlaced;
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float screen_row = o_resolution.x * frag_tex_coord.x;
|
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);
|
color = texture(color_texture, frag_tex_coord);
|
||||||
else
|
} else {
|
||||||
color = texture(color_texture_r, frag_tex_coord);
|
color = texture(color_texture_r, frag_tex_coord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ class Texture;
|
|||||||
constexpr u32 RENDERPASS_COUNT = (MAX_COLOR_FORMATS + 1) * (MAX_DEPTH_FORMATS + 1);
|
constexpr u32 RENDERPASS_COUNT = (MAX_COLOR_FORMATS + 1) * (MAX_DEPTH_FORMATS + 1);
|
||||||
constexpr u32 DESCRIPTOR_BANK_SIZE = 64;
|
constexpr u32 DESCRIPTOR_BANK_SIZE = 64;
|
||||||
|
|
||||||
class Backend : public VideoCore::BackendBase {
|
class Backend final : public VideoCore::BackendBase {
|
||||||
public:
|
public:
|
||||||
Backend(Frontend::EmuWindow& window);
|
Backend(Frontend::EmuWindow& window);
|
||||||
~Backend();
|
~Backend();
|
||||||
|
@ -8,10 +8,7 @@
|
|||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
#include "video_core/pica.h"
|
#include "video_core/pica.h"
|
||||||
#include "video_core/pica_state.h"
|
#include "video_core/pica_state.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/common/renderer.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/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -19,7 +16,7 @@
|
|||||||
|
|
||||||
namespace VideoCore {
|
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_hw_renderer_enabled;
|
||||||
std::atomic<bool> g_shader_jit_enabled;
|
std::atomic<bool> g_shader_jit_enabled;
|
||||||
@ -44,25 +41,14 @@ ResultStatus Init(Frontend::EmuWindow& emu_window, Memory::MemorySystem& memory)
|
|||||||
g_memory = &memory;
|
g_memory = &memory;
|
||||||
Pica::Init();
|
Pica::Init();
|
||||||
|
|
||||||
OpenGL::GLES = Settings::values.use_gles;
|
g_renderer = std::make_unique<DisplayRenderer>(emu_window);
|
||||||
|
return ResultStatus::Success;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shutdown the video core
|
/// Shutdown the video core
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
Pica::Shutdown();
|
Pica::Shutdown();
|
||||||
|
|
||||||
g_renderer->ShutDown();
|
|
||||||
g_renderer.reset();
|
g_renderer.reset();
|
||||||
|
|
||||||
LOG_DEBUG(Render, "shutdown OK");
|
LOG_DEBUG(Render, "shutdown OK");
|
||||||
|
@ -23,8 +23,8 @@ class MemorySystem;
|
|||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
||||||
class RendererBase;
|
class DisplayRenderer;
|
||||||
extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
|
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
|
// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
|
||||||
// qt ui)
|
// qt ui)
|
||||||
|
Reference in New Issue
Block a user