Merge pull request #3301 from ReinUsesLisp/state-tracker
video_core: Remove gl_state and use a state tracker based on dirty flags
This commit is contained in:
commit
22e825a3bc
@ -24,17 +24,29 @@ struct Rectangle {
|
||||
: left(left), top(top), right(right), bottom(bottom) {}
|
||||
|
||||
T GetWidth() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
return std::abs(right - left);
|
||||
} else {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
}
|
||||
}
|
||||
|
||||
T GetHeight() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
||||
if constexpr (std::is_floating_point_v<T>) {
|
||||
return std::abs(bottom - top);
|
||||
} else {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle<T> TranslateX(const T x) const {
|
||||
return Rectangle{left + x, top, right + x, bottom};
|
||||
}
|
||||
|
||||
Rectangle<T> TranslateY(const T y) const {
|
||||
return Rectangle{left, top + y, right, bottom + y};
|
||||
}
|
||||
|
||||
Rectangle<T> Scale(const float s) const {
|
||||
return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
|
||||
static_cast<T>(top + GetHeight() * s)};
|
||||
|
@ -174,6 +174,7 @@ struct System::Impl {
|
||||
}
|
||||
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
||||
gpu_core = VideoCore::CreateGPU(system);
|
||||
renderer->Rasterizer().SetupDirtyFlags();
|
||||
|
||||
is_powered_on = true;
|
||||
exit_lock = false;
|
||||
|
@ -2,6 +2,8 @@ add_library(video_core STATIC
|
||||
buffer_cache/buffer_block.h
|
||||
buffer_cache/buffer_cache.h
|
||||
buffer_cache/map_interval.h
|
||||
dirty_flags.cpp
|
||||
dirty_flags.h
|
||||
dma_pusher.cpp
|
||||
dma_pusher.h
|
||||
engines/const_buffer_engine_interface.h
|
||||
@ -69,8 +71,8 @@ add_library(video_core STATIC
|
||||
renderer_opengl/gl_shader_manager.h
|
||||
renderer_opengl/gl_shader_util.cpp
|
||||
renderer_opengl/gl_shader_util.h
|
||||
renderer_opengl/gl_state.cpp
|
||||
renderer_opengl/gl_state.h
|
||||
renderer_opengl/gl_state_tracker.cpp
|
||||
renderer_opengl/gl_state_tracker.h
|
||||
renderer_opengl/gl_stream_buffer.cpp
|
||||
renderer_opengl/gl_stream_buffer.h
|
||||
renderer_opengl/gl_texture_cache.cpp
|
||||
@ -198,6 +200,8 @@ if (ENABLE_VULKAN)
|
||||
renderer_vulkan/vk_shader_util.h
|
||||
renderer_vulkan/vk_staging_buffer_pool.cpp
|
||||
renderer_vulkan/vk_staging_buffer_pool.h
|
||||
renderer_vulkan/vk_state_tracker.cpp
|
||||
renderer_vulkan/vk_state_tracker.h
|
||||
renderer_vulkan/vk_stream_buffer.cpp
|
||||
renderer_vulkan/vk_stream_buffer.h
|
||||
renderer_vulkan/vk_swapchain.cpp
|
||||
|
46
src/video_core/dirty_flags.cpp
Normal file
46
src/video_core/dirty_flags.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
|
||||
#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
|
||||
#define NUM(field_name) (sizeof(::Tegra::Engines::Maxwell3D::Regs::field_name) / sizeof(u32))
|
||||
|
||||
namespace VideoCommon::Dirty {
|
||||
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
|
||||
void SetupCommonOnWriteStores(Tegra::Engines::Maxwell3D::DirtyState::Flags& store) {
|
||||
store[RenderTargets] = true;
|
||||
store[ZetaBuffer] = true;
|
||||
for (std::size_t i = 0; i < Maxwell3D::Regs::NumRenderTargets; ++i) {
|
||||
store[ColorBuffer0 + i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables) {
|
||||
static constexpr std::size_t num_per_rt = NUM(rt[0]);
|
||||
static constexpr std::size_t begin = OFF(rt);
|
||||
static constexpr std::size_t num = num_per_rt * Maxwell3D::Regs::NumRenderTargets;
|
||||
for (std::size_t rt = 0; rt < Maxwell3D::Regs::NumRenderTargets; ++rt) {
|
||||
FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
|
||||
}
|
||||
FillBlock(tables[1], begin, num, RenderTargets);
|
||||
|
||||
static constexpr std::array zeta_flags{ZetaBuffer, RenderTargets};
|
||||
for (std::size_t i = 0; i < std::size(zeta_flags); ++i) {
|
||||
const u8 flag = zeta_flags[i];
|
||||
auto& table = tables[i];
|
||||
table[OFF(zeta_enable)] = flag;
|
||||
table[OFF(zeta_width)] = flag;
|
||||
table[OFF(zeta_height)] = flag;
|
||||
FillBlock(table, OFF(zeta), NUM(zeta), flag);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Dirty
|
51
src/video_core/dirty_flags.h
Normal file
51
src/video_core/dirty_flags.h
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
|
||||
namespace VideoCommon::Dirty {
|
||||
|
||||
enum : u8 {
|
||||
NullEntry = 0,
|
||||
|
||||
RenderTargets,
|
||||
ColorBuffer0,
|
||||
ColorBuffer1,
|
||||
ColorBuffer2,
|
||||
ColorBuffer3,
|
||||
ColorBuffer4,
|
||||
ColorBuffer5,
|
||||
ColorBuffer6,
|
||||
ColorBuffer7,
|
||||
ZetaBuffer,
|
||||
|
||||
LastCommonEntry,
|
||||
};
|
||||
|
||||
template <typename Integer>
|
||||
void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Table& table, std::size_t begin,
|
||||
std::size_t num, Integer dirty_index) {
|
||||
const auto it = std::begin(table) + begin;
|
||||
std::fill(it, it + num, static_cast<u8>(dirty_index));
|
||||
}
|
||||
|
||||
template <typename Integer1, typename Integer2>
|
||||
void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables, std::size_t begin,
|
||||
std::size_t num, Integer1 index_a, Integer2 index_b) {
|
||||
FillBlock(tables[0], begin, num, index_a);
|
||||
FillBlock(tables[1], begin, num, index_b);
|
||||
}
|
||||
|
||||
void SetupCommonOnWriteStores(Tegra::Engines::Maxwell3D::DirtyState::Flags& store);
|
||||
|
||||
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables);
|
||||
|
||||
} // namespace VideoCommon::Dirty
|
@ -22,7 +22,7 @@ void DmaPusher::DispatchCalls() {
|
||||
MICROPROFILE_SCOPE(DispatchCalls);
|
||||
|
||||
// On entering GPU code, assume all memory may be touched by the ARM core.
|
||||
gpu.Maxwell3D().dirty.OnMemoryWrite();
|
||||
gpu.Maxwell3D().OnMemoryWrite();
|
||||
|
||||
dma_pushbuffer_subindex = 0;
|
||||
|
||||
|
@ -39,7 +39,7 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
|
||||
const bool is_last_call = method_call.IsLastCall();
|
||||
upload_state.ProcessData(method_call.argument, is_last_call);
|
||||
if (is_last_call) {
|
||||
system.GPU().Maxwell3D().dirty.OnMemoryWrite();
|
||||
system.GPU().Maxwell3D().OnMemoryWrite();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
|
||||
const bool is_last_call = method_call.IsLastCall();
|
||||
upload_state.ProcessData(method_call.argument, is_last_call);
|
||||
if (is_last_call) {
|
||||
system.GPU().Maxwell3D().dirty.OnMemoryWrite();
|
||||
system.GPU().Maxwell3D().OnMemoryWrite();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -26,7 +26,8 @@ Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& raste
|
||||
MemoryManager& memory_manager)
|
||||
: system{system}, rasterizer{rasterizer}, memory_manager{memory_manager},
|
||||
macro_interpreter{*this}, upload_state{memory_manager, regs.upload} {
|
||||
InitDirtySettings();
|
||||
dirty.flags.flip();
|
||||
|
||||
InitializeRegisterDefaults();
|
||||
}
|
||||
|
||||
@ -75,8 +76,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.stencil_back_mask = 0xFFFFFFFF;
|
||||
|
||||
regs.depth_test_func = Regs::ComparisonOp::Always;
|
||||
regs.cull.front_face = Regs::Cull::FrontFace::CounterClockWise;
|
||||
regs.cull.cull_face = Regs::Cull::CullFace::Back;
|
||||
regs.front_face = Regs::FrontFace::CounterClockWise;
|
||||
regs.cull_face = Regs::CullFace::Back;
|
||||
|
||||
// TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
|
||||
// register carrying a default value. Assume it's OpenGL's default (1).
|
||||
@ -95,7 +96,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.rasterize_enable = 1;
|
||||
regs.rt_separate_frag_data = 1;
|
||||
regs.framebuffer_srgb = 1;
|
||||
regs.cull.front_face = Maxwell3D::Regs::Cull::FrontFace::ClockWise;
|
||||
regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise;
|
||||
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
|
||||
@ -103,164 +104,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
|
||||
}
|
||||
|
||||
#define DIRTY_REGS_POS(field_name) static_cast<u8>(offsetof(Maxwell3D::DirtyRegs, field_name))
|
||||
|
||||
void Maxwell3D::InitDirtySettings() {
|
||||
const auto set_block = [this](std::size_t start, std::size_t range, u8 position) {
|
||||
const auto start_itr = dirty_pointers.begin() + start;
|
||||
const auto end_itr = start_itr + range;
|
||||
std::fill(start_itr, end_itr, position);
|
||||
};
|
||||
dirty.regs.fill(true);
|
||||
|
||||
// Init Render Targets
|
||||
constexpr u32 registers_per_rt = sizeof(regs.rt[0]) / sizeof(u32);
|
||||
constexpr u32 rt_start_reg = MAXWELL3D_REG_INDEX(rt);
|
||||
constexpr u32 rt_end_reg = rt_start_reg + registers_per_rt * 8;
|
||||
u8 rt_dirty_reg = DIRTY_REGS_POS(render_target);
|
||||
for (u32 rt_reg = rt_start_reg; rt_reg < rt_end_reg; rt_reg += registers_per_rt) {
|
||||
set_block(rt_reg, registers_per_rt, rt_dirty_reg);
|
||||
++rt_dirty_reg;
|
||||
}
|
||||
constexpr u32 depth_buffer_flag = DIRTY_REGS_POS(depth_buffer);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(zeta_enable)] = depth_buffer_flag;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(zeta_width)] = depth_buffer_flag;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(zeta_height)] = depth_buffer_flag;
|
||||
constexpr u32 registers_in_zeta = sizeof(regs.zeta) / sizeof(u32);
|
||||
constexpr u32 zeta_reg = MAXWELL3D_REG_INDEX(zeta);
|
||||
set_block(zeta_reg, registers_in_zeta, depth_buffer_flag);
|
||||
|
||||
// Init Vertex Arrays
|
||||
constexpr u32 vertex_array_start = MAXWELL3D_REG_INDEX(vertex_array);
|
||||
constexpr u32 vertex_array_size = sizeof(regs.vertex_array[0]) / sizeof(u32);
|
||||
constexpr u32 vertex_array_end = vertex_array_start + vertex_array_size * Regs::NumVertexArrays;
|
||||
u8 va_dirty_reg = DIRTY_REGS_POS(vertex_array);
|
||||
u8 vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
|
||||
for (u32 vertex_reg = vertex_array_start; vertex_reg < vertex_array_end;
|
||||
vertex_reg += vertex_array_size) {
|
||||
set_block(vertex_reg, 3, va_dirty_reg);
|
||||
// The divisor concerns vertex array instances
|
||||
dirty_pointers[static_cast<std::size_t>(vertex_reg) + 3] = vi_dirty_reg;
|
||||
++va_dirty_reg;
|
||||
++vi_dirty_reg;
|
||||
}
|
||||
constexpr u32 vertex_limit_start = MAXWELL3D_REG_INDEX(vertex_array_limit);
|
||||
constexpr u32 vertex_limit_size = sizeof(regs.vertex_array_limit[0]) / sizeof(u32);
|
||||
constexpr u32 vertex_limit_end = vertex_limit_start + vertex_limit_size * Regs::NumVertexArrays;
|
||||
va_dirty_reg = DIRTY_REGS_POS(vertex_array);
|
||||
for (u32 vertex_reg = vertex_limit_start; vertex_reg < vertex_limit_end;
|
||||
vertex_reg += vertex_limit_size) {
|
||||
set_block(vertex_reg, vertex_limit_size, va_dirty_reg);
|
||||
va_dirty_reg++;
|
||||
}
|
||||
constexpr u32 vertex_instance_start = MAXWELL3D_REG_INDEX(instanced_arrays);
|
||||
constexpr u32 vertex_instance_size =
|
||||
sizeof(regs.instanced_arrays.is_instanced[0]) / sizeof(u32);
|
||||
constexpr u32 vertex_instance_end =
|
||||
vertex_instance_start + vertex_instance_size * Regs::NumVertexArrays;
|
||||
vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
|
||||
for (u32 vertex_reg = vertex_instance_start; vertex_reg < vertex_instance_end;
|
||||
vertex_reg += vertex_instance_size) {
|
||||
set_block(vertex_reg, vertex_instance_size, vi_dirty_reg);
|
||||
vi_dirty_reg++;
|
||||
}
|
||||
set_block(MAXWELL3D_REG_INDEX(vertex_attrib_format), regs.vertex_attrib_format.size(),
|
||||
DIRTY_REGS_POS(vertex_attrib_format));
|
||||
|
||||
// Init Shaders
|
||||
constexpr u32 shader_registers_count =
|
||||
sizeof(regs.shader_config[0]) * Regs::MaxShaderProgram / sizeof(u32);
|
||||
set_block(MAXWELL3D_REG_INDEX(shader_config[0]), shader_registers_count,
|
||||
DIRTY_REGS_POS(shaders));
|
||||
|
||||
// State
|
||||
|
||||
// Viewport
|
||||
constexpr u8 viewport_dirty_reg = DIRTY_REGS_POS(viewport);
|
||||
constexpr u32 viewport_start = MAXWELL3D_REG_INDEX(viewports);
|
||||
constexpr u32 viewport_size = sizeof(regs.viewports) / sizeof(u32);
|
||||
set_block(viewport_start, viewport_size, viewport_dirty_reg);
|
||||
constexpr u32 view_volume_start = MAXWELL3D_REG_INDEX(view_volume_clip_control);
|
||||
constexpr u32 view_volume_size = sizeof(regs.view_volume_clip_control) / sizeof(u32);
|
||||
set_block(view_volume_start, view_volume_size, viewport_dirty_reg);
|
||||
|
||||
// Viewport transformation
|
||||
constexpr u32 viewport_trans_start = MAXWELL3D_REG_INDEX(viewport_transform);
|
||||
constexpr u32 viewport_trans_size = sizeof(regs.viewport_transform) / sizeof(u32);
|
||||
set_block(viewport_trans_start, viewport_trans_size, DIRTY_REGS_POS(viewport_transform));
|
||||
|
||||
// Cullmode
|
||||
constexpr u32 cull_mode_start = MAXWELL3D_REG_INDEX(cull);
|
||||
constexpr u32 cull_mode_size = sizeof(regs.cull) / sizeof(u32);
|
||||
set_block(cull_mode_start, cull_mode_size, DIRTY_REGS_POS(cull_mode));
|
||||
|
||||
// Screen y control
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(screen_y_control)] = DIRTY_REGS_POS(screen_y_control);
|
||||
|
||||
// Primitive Restart
|
||||
constexpr u32 primitive_restart_start = MAXWELL3D_REG_INDEX(primitive_restart);
|
||||
constexpr u32 primitive_restart_size = sizeof(regs.primitive_restart) / sizeof(u32);
|
||||
set_block(primitive_restart_start, primitive_restart_size, DIRTY_REGS_POS(primitive_restart));
|
||||
|
||||
// Depth Test
|
||||
constexpr u8 depth_test_dirty_reg = DIRTY_REGS_POS(depth_test);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_enable)] = depth_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_write_enabled)] = depth_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_func)] = depth_test_dirty_reg;
|
||||
|
||||
// Stencil Test
|
||||
constexpr u32 stencil_test_dirty_reg = DIRTY_REGS_POS(stencil_test);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_enable)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_func)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_ref)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_mask)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_fail)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zfail)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zpass)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_mask)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_two_side_enable)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_func)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_ref)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_mask)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_fail)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zfail)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zpass)] = stencil_test_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_mask)] = stencil_test_dirty_reg;
|
||||
|
||||
// Color Mask
|
||||
constexpr u8 color_mask_dirty_reg = DIRTY_REGS_POS(color_mask);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(color_mask_common)] = color_mask_dirty_reg;
|
||||
set_block(MAXWELL3D_REG_INDEX(color_mask), sizeof(regs.color_mask) / sizeof(u32),
|
||||
color_mask_dirty_reg);
|
||||
// Blend State
|
||||
constexpr u8 blend_state_dirty_reg = DIRTY_REGS_POS(blend_state);
|
||||
set_block(MAXWELL3D_REG_INDEX(blend_color), sizeof(regs.blend_color) / sizeof(u32),
|
||||
blend_state_dirty_reg);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(independent_blend_enable)] = blend_state_dirty_reg;
|
||||
set_block(MAXWELL3D_REG_INDEX(blend), sizeof(regs.blend) / sizeof(u32), blend_state_dirty_reg);
|
||||
set_block(MAXWELL3D_REG_INDEX(independent_blend), sizeof(regs.independent_blend) / sizeof(u32),
|
||||
blend_state_dirty_reg);
|
||||
|
||||
// Scissor State
|
||||
constexpr u8 scissor_test_dirty_reg = DIRTY_REGS_POS(scissor_test);
|
||||
set_block(MAXWELL3D_REG_INDEX(scissor_test), sizeof(regs.scissor_test) / sizeof(u32),
|
||||
scissor_test_dirty_reg);
|
||||
|
||||
// Polygon Offset
|
||||
constexpr u8 polygon_offset_dirty_reg = DIRTY_REGS_POS(polygon_offset);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_fill_enable)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_line_enable)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_point_enable)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_units)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_factor)] = polygon_offset_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_clamp)] = polygon_offset_dirty_reg;
|
||||
|
||||
// Depth bounds
|
||||
constexpr u8 depth_bounds_values_dirty_reg = DIRTY_REGS_POS(depth_bounds_values);
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[0])] = depth_bounds_values_dirty_reg;
|
||||
dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[1])] = depth_bounds_values_dirty_reg;
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) {
|
||||
// Reset the current macro.
|
||||
executing_macro = 0;
|
||||
@ -319,19 +162,9 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
|
||||
if (regs.reg_array[method] != method_call.argument) {
|
||||
regs.reg_array[method] = method_call.argument;
|
||||
const std::size_t dirty_reg = dirty_pointers[method];
|
||||
if (dirty_reg) {
|
||||
dirty.regs[dirty_reg] = true;
|
||||
if (dirty_reg >= DIRTY_REGS_POS(vertex_array) &&
|
||||
dirty_reg < DIRTY_REGS_POS(vertex_array_buffers)) {
|
||||
dirty.vertex_array_buffers = true;
|
||||
} else if (dirty_reg >= DIRTY_REGS_POS(vertex_instance) &&
|
||||
dirty_reg < DIRTY_REGS_POS(vertex_instances)) {
|
||||
dirty.vertex_instances = true;
|
||||
} else if (dirty_reg >= DIRTY_REGS_POS(render_target) &&
|
||||
dirty_reg < DIRTY_REGS_POS(render_settings)) {
|
||||
dirty.render_settings = true;
|
||||
}
|
||||
|
||||
for (const auto& table : dirty.tables) {
|
||||
dirty.flags[table[method]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -419,7 +252,7 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
const bool is_last_call = method_call.IsLastCall();
|
||||
upload_state.ProcessData(method_call.argument, is_last_call);
|
||||
if (is_last_call) {
|
||||
dirty.OnMemoryWrite();
|
||||
OnMemoryWrite();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -727,7 +560,7 @@ void Maxwell3D::FinishCBData() {
|
||||
|
||||
const u32 id = cb_data_state.id;
|
||||
memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size);
|
||||
dirty.OnMemoryWrite();
|
||||
OnMemoryWrite();
|
||||
|
||||
cb_data_state.id = null_cb_data;
|
||||
cb_data_state.current = null_cb_data;
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
@ -431,21 +432,15 @@ public:
|
||||
GeneratedPrimitives = 0x1F,
|
||||
};
|
||||
|
||||
struct Cull {
|
||||
enum class FrontFace : u32 {
|
||||
ClockWise = 0x0900,
|
||||
CounterClockWise = 0x0901,
|
||||
};
|
||||
enum class FrontFace : u32 {
|
||||
ClockWise = 0x0900,
|
||||
CounterClockWise = 0x0901,
|
||||
};
|
||||
|
||||
enum class CullFace : u32 {
|
||||
Front = 0x0404,
|
||||
Back = 0x0405,
|
||||
FrontAndBack = 0x0408,
|
||||
};
|
||||
|
||||
u32 enabled;
|
||||
FrontFace front_face;
|
||||
CullFace cull_face;
|
||||
enum class CullFace : u32 {
|
||||
Front = 0x0404,
|
||||
Back = 0x0405,
|
||||
FrontAndBack = 0x0408,
|
||||
};
|
||||
|
||||
struct Blend {
|
||||
@ -574,7 +569,7 @@ public:
|
||||
f32 translate_z;
|
||||
INSERT_UNION_PADDING_WORDS(2);
|
||||
|
||||
Common::Rectangle<s32> GetRect() const {
|
||||
Common::Rectangle<f32> GetRect() const {
|
||||
return {
|
||||
GetX(), // left
|
||||
GetY() + GetHeight(), // top
|
||||
@ -583,20 +578,20 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
s32 GetX() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
|
||||
f32 GetX() const {
|
||||
return std::max(0.0f, translate_x - std::fabs(scale_x));
|
||||
}
|
||||
|
||||
s32 GetY() const {
|
||||
return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
|
||||
f32 GetY() const {
|
||||
return std::max(0.0f, translate_y - std::fabs(scale_y));
|
||||
}
|
||||
|
||||
s32 GetWidth() const {
|
||||
return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
|
||||
f32 GetWidth() const {
|
||||
return translate_x + std::fabs(scale_x) - GetX();
|
||||
}
|
||||
|
||||
s32 GetHeight() const {
|
||||
return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
|
||||
f32 GetHeight() const {
|
||||
return translate_y + std::fabs(scale_y) - GetY();
|
||||
}
|
||||
};
|
||||
|
||||
@ -872,16 +867,7 @@ public:
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x35);
|
||||
|
||||
union {
|
||||
BitField<0, 1, u32> c0;
|
||||
BitField<1, 1, u32> c1;
|
||||
BitField<2, 1, u32> c2;
|
||||
BitField<3, 1, u32> c3;
|
||||
BitField<4, 1, u32> c4;
|
||||
BitField<5, 1, u32> c5;
|
||||
BitField<6, 1, u32> c6;
|
||||
BitField<7, 1, u32> c7;
|
||||
} clip_distance_enabled;
|
||||
u32 clip_distance_enabled;
|
||||
|
||||
u32 samplecnt_enable;
|
||||
|
||||
@ -1060,7 +1046,9 @@ public:
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(1);
|
||||
|
||||
Cull cull;
|
||||
u32 cull_test_enabled;
|
||||
FrontFace front_face;
|
||||
CullFace cull_face;
|
||||
|
||||
u32 pixel_center_integer;
|
||||
|
||||
@ -1238,79 +1226,6 @@ public:
|
||||
|
||||
State state{};
|
||||
|
||||
struct DirtyRegs {
|
||||
static constexpr std::size_t NUM_REGS = 256;
|
||||
static_assert(NUM_REGS - 1 <= std::numeric_limits<u8>::max());
|
||||
|
||||
union {
|
||||
struct {
|
||||
bool null_dirty;
|
||||
|
||||
// Vertex Attributes
|
||||
bool vertex_attrib_format;
|
||||
|
||||
// Vertex Arrays
|
||||
std::array<bool, 32> vertex_array;
|
||||
|
||||
bool vertex_array_buffers;
|
||||
|
||||
// Vertex Instances
|
||||
std::array<bool, 32> vertex_instance;
|
||||
|
||||
bool vertex_instances;
|
||||
|
||||
// Render Targets
|
||||
std::array<bool, 8> render_target;
|
||||
bool depth_buffer;
|
||||
|
||||
bool render_settings;
|
||||
|
||||
// Shaders
|
||||
bool shaders;
|
||||
|
||||
// Rasterizer State
|
||||
bool viewport;
|
||||
bool clip_coefficient;
|
||||
bool cull_mode;
|
||||
bool primitive_restart;
|
||||
bool depth_test;
|
||||
bool stencil_test;
|
||||
bool blend_state;
|
||||
bool scissor_test;
|
||||
bool transform_feedback;
|
||||
bool color_mask;
|
||||
bool polygon_offset;
|
||||
bool depth_bounds_values;
|
||||
|
||||
// Complementary
|
||||
bool viewport_transform;
|
||||
bool screen_y_control;
|
||||
|
||||
bool memory_general;
|
||||
};
|
||||
std::array<bool, NUM_REGS> regs;
|
||||
};
|
||||
|
||||
void ResetVertexArrays() {
|
||||
vertex_array.fill(true);
|
||||
vertex_array_buffers = true;
|
||||
}
|
||||
|
||||
void ResetRenderTargets() {
|
||||
depth_buffer = true;
|
||||
render_target.fill(true);
|
||||
render_settings = true;
|
||||
}
|
||||
|
||||
void OnMemoryWrite() {
|
||||
shaders = true;
|
||||
memory_general = true;
|
||||
ResetRenderTargets();
|
||||
ResetVertexArrays();
|
||||
}
|
||||
|
||||
} dirty{};
|
||||
|
||||
/// Reads a register value located at the input method address
|
||||
u32 GetRegisterValue(u32 method) const;
|
||||
|
||||
@ -1356,6 +1271,11 @@ public:
|
||||
return execute_on;
|
||||
}
|
||||
|
||||
/// Notify a memory write has happened.
|
||||
void OnMemoryWrite() {
|
||||
dirty.flags |= dirty.on_write_stores;
|
||||
}
|
||||
|
||||
enum class MMEDrawMode : u32 {
|
||||
Undefined,
|
||||
Array,
|
||||
@ -1371,6 +1291,16 @@ public:
|
||||
u32 gl_end_count{};
|
||||
} mme_draw;
|
||||
|
||||
struct DirtyState {
|
||||
using Flags = std::bitset<std::numeric_limits<u8>::max()>;
|
||||
using Table = std::array<u8, Regs::NUM_REGS>;
|
||||
using Tables = std::array<Table, 2>;
|
||||
|
||||
Flags flags;
|
||||
Flags on_write_stores;
|
||||
Tables tables{};
|
||||
} dirty;
|
||||
|
||||
private:
|
||||
void InitializeRegisterDefaults();
|
||||
|
||||
@ -1417,8 +1347,6 @@ private:
|
||||
/// Retrieves information about a specific TSC entry from the TSC buffer.
|
||||
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
|
||||
|
||||
void InitDirtySettings();
|
||||
|
||||
/**
|
||||
* Call a macro on this engine.
|
||||
* @param method Method to call
|
||||
@ -1561,7 +1489,9 @@ ASSERT_REG_POSITION(index_array, 0x5F2);
|
||||
ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
|
||||
ASSERT_REG_POSITION(instanced_arrays, 0x620);
|
||||
ASSERT_REG_POSITION(vp_point_size, 0x644);
|
||||
ASSERT_REG_POSITION(cull, 0x646);
|
||||
ASSERT_REG_POSITION(cull_test_enabled, 0x646);
|
||||
ASSERT_REG_POSITION(front_face, 0x647);
|
||||
ASSERT_REG_POSITION(cull_face, 0x648);
|
||||
ASSERT_REG_POSITION(pixel_center_integer, 0x649);
|
||||
ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
|
||||
ASSERT_REG_POSITION(view_volume_clip_control, 0x64F);
|
||||
|
@ -57,7 +57,7 @@ void MaxwellDMA::HandleCopy() {
|
||||
}
|
||||
|
||||
// All copies here update the main memory, so mark all rasterizer states as invalid.
|
||||
system.GPU().Maxwell3D().dirty.OnMemoryWrite();
|
||||
system.GPU().Maxwell3D().OnMemoryWrite();
|
||||
|
||||
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
|
||||
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
|
||||
|
@ -89,6 +89,9 @@ public:
|
||||
virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
|
||||
const DiskResourceLoadCallback& callback = {}) {}
|
||||
|
||||
/// Initializes renderer dirty flags
|
||||
virtual void SetupDirtyFlags() {}
|
||||
|
||||
/// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
|
||||
GuestDriverProfile& AccessGuestDriverProfile() {
|
||||
return guest_driver_profile;
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@ -36,8 +35,7 @@ OGLFramebuffer FramebufferCacheOpenGL::CreateFramebuffer(const FramebufferCacheK
|
||||
framebuffer.Create();
|
||||
|
||||
// TODO(Rodrigo): Use DSA here after Nvidia fixes their framebuffer DSA bugs.
|
||||
local_state.draw.draw_framebuffer = framebuffer.handle;
|
||||
local_state.ApplyFramebufferState();
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
|
||||
|
||||
if (key.zeta) {
|
||||
const bool stencil = key.zeta->GetSurfaceParams().type == SurfaceType::DepthStencil;
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
|
||||
namespace OpenGL {
|
||||
@ -63,7 +62,6 @@ public:
|
||||
private:
|
||||
OGLFramebuffer CreateFramebuffer(const FramebufferCacheKey& key);
|
||||
|
||||
OpenGLState local_state;
|
||||
std::unordered_map<FramebufferCacheKey, OGLFramebuffer> cache;
|
||||
};
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,7 @@
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
@ -55,7 +55,8 @@ struct DrawParameters;
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
|
||||
public:
|
||||
explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
ScreenInfo& info);
|
||||
ScreenInfo& info, GLShader::ProgramManager& program_manager,
|
||||
StateTracker& state_tracker);
|
||||
~RasterizerOpenGL() override;
|
||||
|
||||
void Draw(bool is_indexed, bool is_instanced) override;
|
||||
@ -76,6 +77,7 @@ public:
|
||||
u32 pixel_stride) override;
|
||||
void LoadDiskResources(const std::atomic_bool& stop_loading,
|
||||
const VideoCore::DiskResourceLoadCallback& callback) override;
|
||||
void SetupDirtyFlags() override;
|
||||
|
||||
/// Returns true when there are commands queued to the OpenGL server.
|
||||
bool AnyCommandQueued() const {
|
||||
@ -86,8 +88,7 @@ private:
|
||||
/// Configures the color and depth framebuffer states.
|
||||
void ConfigureFramebuffers();
|
||||
|
||||
void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
|
||||
bool using_depth_fb, bool using_stencil_fb);
|
||||
void ConfigureClearFramebuffer(bool using_color_fb, bool using_depth_fb, bool using_stencil_fb);
|
||||
|
||||
/// Configures the current constbuffers to use for the draw command.
|
||||
void SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader);
|
||||
@ -130,11 +131,13 @@ private:
|
||||
const GLShader::ImageEntry& entry);
|
||||
|
||||
/// Syncs the viewport and depth range to match the guest state
|
||||
void SyncViewport(OpenGLState& current_state);
|
||||
void SyncViewport();
|
||||
|
||||
/// Syncs the depth clamp state
|
||||
void SyncDepthClamp();
|
||||
|
||||
/// Syncs the clip enabled status to match the guest state
|
||||
void SyncClipEnabled(
|
||||
const std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances>& clip_mask);
|
||||
void SyncClipEnabled(u32 clip_mask);
|
||||
|
||||
/// Syncs the clip coefficients to match the guest state
|
||||
void SyncClipCoef();
|
||||
@ -164,7 +167,7 @@ private:
|
||||
void SyncMultiSampleState();
|
||||
|
||||
/// Syncs the scissor test state to match the guest state
|
||||
void SyncScissorTest(OpenGLState& current_state);
|
||||
void SyncScissorTest();
|
||||
|
||||
/// Syncs the transform feedback state to match the guest state
|
||||
void SyncTransformFeedback();
|
||||
@ -173,7 +176,7 @@ private:
|
||||
void SyncPointState();
|
||||
|
||||
/// Syncs the rasterizer enable state to match the guest state
|
||||
void SyncRasterizeEnable(OpenGLState& current_state);
|
||||
void SyncRasterizeEnable();
|
||||
|
||||
/// Syncs Color Mask
|
||||
void SyncColorMask();
|
||||
@ -184,6 +187,9 @@ private:
|
||||
/// Syncs the alpha test state to match the guest state
|
||||
void SyncAlphaTest();
|
||||
|
||||
/// Syncs the framebuffer sRGB state to match the guest state
|
||||
void SyncFramebufferSRGB();
|
||||
|
||||
/// Check for extension that are not strictly required but are needed for correct emulation
|
||||
void CheckExtensions();
|
||||
|
||||
@ -191,18 +197,17 @@ private:
|
||||
|
||||
std::size_t CalculateIndexBufferSize() const;
|
||||
|
||||
/// Updates and returns a vertex array object representing current vertex format
|
||||
GLuint SetupVertexFormat();
|
||||
/// Updates the current vertex format
|
||||
void SetupVertexFormat();
|
||||
|
||||
void SetupVertexBuffer(GLuint vao);
|
||||
void SetupVertexInstances(GLuint vao);
|
||||
void SetupVertexBuffer();
|
||||
void SetupVertexInstances();
|
||||
|
||||
GLintptr SetupIndexBuffer();
|
||||
|
||||
void SetupShaders(GLenum primitive_mode);
|
||||
|
||||
const Device device;
|
||||
OpenGLState state;
|
||||
|
||||
TextureCacheOpenGL texture_cache;
|
||||
ShaderCacheOpenGL shader_cache;
|
||||
@ -212,22 +217,20 @@ private:
|
||||
|
||||
Core::System& system;
|
||||
ScreenInfo& screen_info;
|
||||
|
||||
std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
|
||||
std::map<std::array<Tegra::Engines::Maxwell3D::Regs::VertexAttribute,
|
||||
Tegra::Engines::Maxwell3D::Regs::NumVertexAttributes>,
|
||||
OGLVertexArray>
|
||||
vertex_array_cache;
|
||||
GLShader::ProgramManager& program_manager;
|
||||
StateTracker& state_tracker;
|
||||
|
||||
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
OGLBufferCache buffer_cache;
|
||||
|
||||
VertexArrayPushBuffer vertex_array_pushbuffer;
|
||||
VertexArrayPushBuffer vertex_array_pushbuffer{state_tracker};
|
||||
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
|
||||
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
|
||||
|
||||
/// Number of commands queued to the OpenGL driver. Reseted on flush.
|
||||
std::size_t num_queued_commands = 0;
|
||||
|
||||
u32 last_clip_distance_mask = 0;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_util.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192));
|
||||
@ -20,7 +19,7 @@ void OGLRenderbuffer::Create() {
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
|
||||
glGenRenderbuffers(1, &handle);
|
||||
glCreateRenderbuffers(1, &handle);
|
||||
}
|
||||
|
||||
void OGLRenderbuffer::Release() {
|
||||
@ -29,7 +28,6 @@ void OGLRenderbuffer::Release() {
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteRenderbuffers(1, &handle);
|
||||
OpenGLState::GetCurState().ResetRenderbuffer(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@ -47,7 +45,6 @@ void OGLTexture::Release() {
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteTextures(1, &handle);
|
||||
OpenGLState::GetCurState().UnbindTexture(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@ -65,7 +62,6 @@ void OGLTextureView::Release() {
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteTextures(1, &handle);
|
||||
OpenGLState::GetCurState().UnbindTexture(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@ -83,7 +79,6 @@ void OGLSampler::Release() {
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteSamplers(1, &handle);
|
||||
OpenGLState::GetCurState().ResetSampler(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@ -127,7 +122,6 @@ void OGLProgram::Release() {
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteProgram(handle);
|
||||
OpenGLState::GetCurState().ResetProgram(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@ -145,7 +139,6 @@ void OGLPipeline::Release() {
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteProgramPipelines(1, &handle);
|
||||
OpenGLState::GetCurState().ResetPipeline(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
@ -189,24 +182,6 @@ void OGLSync::Release() {
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
void OGLVertexArray::Create() {
|
||||
if (handle != 0)
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
|
||||
glCreateVertexArrays(1, &handle);
|
||||
}
|
||||
|
||||
void OGLVertexArray::Release() {
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteVertexArrays(1, &handle);
|
||||
OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
void OGLFramebuffer::Create() {
|
||||
if (handle != 0)
|
||||
return;
|
||||
@ -221,7 +196,6 @@ void OGLFramebuffer::Release() {
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
|
||||
glDeleteFramebuffers(1, &handle);
|
||||
OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
@ -241,31 +241,6 @@ public:
|
||||
GLsync handle = 0;
|
||||
};
|
||||
|
||||
class OGLVertexArray : private NonCopyable {
|
||||
public:
|
||||
OGLVertexArray() = default;
|
||||
|
||||
OGLVertexArray(OGLVertexArray&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
|
||||
|
||||
~OGLVertexArray() {
|
||||
Release();
|
||||
}
|
||||
|
||||
OGLVertexArray& operator=(OGLVertexArray&& o) noexcept {
|
||||
Release();
|
||||
handle = std::exchange(o.handle, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Creates a new internal OpenGL resource and stores the handle
|
||||
void Create();
|
||||
|
||||
/// Deletes the internal OpenGL resource
|
||||
void Release();
|
||||
|
||||
GLuint handle = 0;
|
||||
};
|
||||
|
||||
class OGLFramebuffer : private NonCopyable {
|
||||
public:
|
||||
OGLFramebuffer() = default;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@ -623,7 +624,7 @@ bool ShaderCacheOpenGL::GenerateUnspecializedShaders(
|
||||
}
|
||||
|
||||
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
|
||||
if (!system.GPU().Maxwell3D().dirty.shaders) {
|
||||
if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) {
|
||||
return last_shaders[static_cast<std::size_t>(program)];
|
||||
}
|
||||
|
||||
|
@ -2547,7 +2547,10 @@ ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
|
||||
for (const auto& image : ir.GetImages()) {
|
||||
entries.images.emplace_back(image);
|
||||
}
|
||||
entries.clip_distances = ir.GetClipDistances();
|
||||
const auto clip_distances = ir.GetClipDistances();
|
||||
for (std::size_t i = 0; i < std::size(clip_distances); ++i) {
|
||||
entries.clip_distances = (clip_distances[i] ? 1U : 0U) << i;
|
||||
}
|
||||
entries.shader_length = ir.GetLength();
|
||||
return entries;
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ struct ShaderEntries {
|
||||
std::vector<GlobalMemoryEntry> global_memory_entries;
|
||||
std::vector<SamplerEntry> samplers;
|
||||
std::vector<ImageEntry> images;
|
||||
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
|
||||
u32 clip_distances{};
|
||||
std::size_t shader_length{};
|
||||
};
|
||||
|
||||
|
@ -10,27 +10,21 @@ namespace OpenGL::GLShader {
|
||||
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
|
||||
ProgramManager::ProgramManager() {
|
||||
ProgramManager::~ProgramManager() = default;
|
||||
|
||||
void ProgramManager::Create() {
|
||||
pipeline.Create();
|
||||
}
|
||||
|
||||
ProgramManager::~ProgramManager() = default;
|
||||
|
||||
void ProgramManager::ApplyTo(OpenGLState& state) {
|
||||
UpdatePipeline();
|
||||
state.draw.shader_program = 0;
|
||||
state.draw.program_pipeline = pipeline.handle;
|
||||
}
|
||||
|
||||
void ProgramManager::UpdatePipeline() {
|
||||
void ProgramManager::Update() {
|
||||
// Avoid updating the pipeline when values have no changed
|
||||
if (old_state == current_state) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Workaround for AMD bug
|
||||
constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT |
|
||||
GL_FRAGMENT_SHADER_BIT};
|
||||
static constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT |
|
||||
GL_FRAGMENT_SHADER_BIT};
|
||||
glUseProgramStages(pipeline.handle, all_used_stages, 0);
|
||||
|
||||
glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader);
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/maxwell_to_gl.h"
|
||||
|
||||
namespace OpenGL::GLShader {
|
||||
@ -29,25 +28,26 @@ static_assert(sizeof(MaxwellUniformData) < 16384,
|
||||
|
||||
class ProgramManager {
|
||||
public:
|
||||
explicit ProgramManager();
|
||||
~ProgramManager();
|
||||
|
||||
void ApplyTo(OpenGLState& state);
|
||||
void Create();
|
||||
|
||||
void UseProgrammableVertexShader(GLuint program) {
|
||||
void Update();
|
||||
|
||||
void UseVertexShader(GLuint program) {
|
||||
current_state.vertex_shader = program;
|
||||
}
|
||||
|
||||
void UseProgrammableGeometryShader(GLuint program) {
|
||||
void UseGeometryShader(GLuint program) {
|
||||
current_state.geometry_shader = program;
|
||||
}
|
||||
|
||||
void UseProgrammableFragmentShader(GLuint program) {
|
||||
void UseFragmentShader(GLuint program) {
|
||||
current_state.fragment_shader = program;
|
||||
}
|
||||
|
||||
void UseTrivialGeometryShader() {
|
||||
current_state.geometry_shader = 0;
|
||||
GLuint GetHandle() const {
|
||||
return pipeline.handle;
|
||||
}
|
||||
|
||||
void UseTrivialFragmentShader() {
|
||||
@ -70,8 +70,6 @@ private:
|
||||
GLuint geometry_shader{};
|
||||
};
|
||||
|
||||
void UpdatePipeline();
|
||||
|
||||
OGLPipeline pipeline;
|
||||
PipelineState current_state;
|
||||
PipelineState old_state;
|
||||
|
@ -1,569 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <glad/glad.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_State, "OpenGL", "State Change", MP_RGB(192, 128, 128));
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
OpenGLState OpenGLState::cur_state;
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
bool UpdateValue(T& current_value, const T new_value) {
|
||||
const bool changed = current_value != new_value;
|
||||
current_value = new_value;
|
||||
return changed;
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool UpdateTie(T1 current_value, const T2 new_value) {
|
||||
const bool changed = current_value != new_value;
|
||||
current_value = new_value;
|
||||
return changed;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<std::pair<GLuint, GLsizei>> UpdateArray(T& current_values, const T& new_values) {
|
||||
std::optional<std::size_t> first;
|
||||
std::size_t last;
|
||||
for (std::size_t i = 0; i < std::size(current_values); ++i) {
|
||||
if (!UpdateValue(current_values[i], new_values[i])) {
|
||||
continue;
|
||||
}
|
||||
if (!first) {
|
||||
first = i;
|
||||
}
|
||||
last = i;
|
||||
}
|
||||
if (!first) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::make_pair(static_cast<GLuint>(*first), static_cast<GLsizei>(last - *first + 1));
|
||||
}
|
||||
|
||||
void Enable(GLenum cap, bool enable) {
|
||||
if (enable) {
|
||||
glEnable(cap);
|
||||
} else {
|
||||
glDisable(cap);
|
||||
}
|
||||
}
|
||||
|
||||
void Enable(GLenum cap, GLuint index, bool enable) {
|
||||
if (enable) {
|
||||
glEnablei(cap, index);
|
||||
} else {
|
||||
glDisablei(cap, index);
|
||||
}
|
||||
}
|
||||
|
||||
void Enable(GLenum cap, bool& current_value, bool new_value) {
|
||||
if (UpdateValue(current_value, new_value)) {
|
||||
Enable(cap, new_value);
|
||||
}
|
||||
}
|
||||
|
||||
void Enable(GLenum cap, GLuint index, bool& current_value, bool new_value) {
|
||||
if (UpdateValue(current_value, new_value)) {
|
||||
Enable(cap, index, new_value);
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
OpenGLState::OpenGLState() = default;
|
||||
|
||||
void OpenGLState::SetDefaultViewports() {
|
||||
viewports.fill(Viewport{});
|
||||
|
||||
depth_clamp.far_plane = false;
|
||||
depth_clamp.near_plane = false;
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyFramebufferState() {
|
||||
if (UpdateValue(cur_state.draw.read_framebuffer, draw.read_framebuffer)) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
|
||||
}
|
||||
if (UpdateValue(cur_state.draw.draw_framebuffer, draw.draw_framebuffer)) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyVertexArrayState() {
|
||||
if (UpdateValue(cur_state.draw.vertex_array, draw.vertex_array)) {
|
||||
glBindVertexArray(draw.vertex_array);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyShaderProgram() {
|
||||
if (UpdateValue(cur_state.draw.shader_program, draw.shader_program)) {
|
||||
glUseProgram(draw.shader_program);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyProgramPipeline() {
|
||||
if (UpdateValue(cur_state.draw.program_pipeline, draw.program_pipeline)) {
|
||||
glBindProgramPipeline(draw.program_pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyClipDistances() {
|
||||
for (std::size_t i = 0; i < clip_distance.size(); ++i) {
|
||||
Enable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i), cur_state.clip_distance[i],
|
||||
clip_distance[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyPointSize() {
|
||||
Enable(GL_PROGRAM_POINT_SIZE, cur_state.point.program_control, point.program_control);
|
||||
Enable(GL_POINT_SPRITE, cur_state.point.sprite, point.sprite);
|
||||
if (UpdateValue(cur_state.point.size, point.size)) {
|
||||
glPointSize(point.size);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyFragmentColorClamp() {
|
||||
if (UpdateValue(cur_state.fragment_color_clamp.enabled, fragment_color_clamp.enabled)) {
|
||||
glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
|
||||
fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyMultisample() {
|
||||
Enable(GL_SAMPLE_ALPHA_TO_COVERAGE, cur_state.multisample_control.alpha_to_coverage,
|
||||
multisample_control.alpha_to_coverage);
|
||||
Enable(GL_SAMPLE_ALPHA_TO_ONE, cur_state.multisample_control.alpha_to_one,
|
||||
multisample_control.alpha_to_one);
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyDepthClamp() {
|
||||
if (depth_clamp.far_plane == cur_state.depth_clamp.far_plane &&
|
||||
depth_clamp.near_plane == cur_state.depth_clamp.near_plane) {
|
||||
return;
|
||||
}
|
||||
cur_state.depth_clamp = depth_clamp;
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(depth_clamp.far_plane != depth_clamp.near_plane,
|
||||
"Unimplemented Depth Clamp Separation!");
|
||||
|
||||
Enable(GL_DEPTH_CLAMP, depth_clamp.far_plane || depth_clamp.near_plane);
|
||||
}
|
||||
|
||||
void OpenGLState::ApplySRgb() {
|
||||
if (cur_state.framebuffer_srgb.enabled == framebuffer_srgb.enabled)
|
||||
return;
|
||||
cur_state.framebuffer_srgb.enabled = framebuffer_srgb.enabled;
|
||||
if (framebuffer_srgb.enabled) {
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
} else {
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyCulling() {
|
||||
Enable(GL_CULL_FACE, cur_state.cull.enabled, cull.enabled);
|
||||
|
||||
if (UpdateValue(cur_state.cull.mode, cull.mode)) {
|
||||
glCullFace(cull.mode);
|
||||
}
|
||||
|
||||
if (UpdateValue(cur_state.cull.front_face, cull.front_face)) {
|
||||
glFrontFace(cull.front_face);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyRasterizerDiscard() {
|
||||
Enable(GL_RASTERIZER_DISCARD, cur_state.rasterizer_discard, rasterizer_discard);
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyColorMask() {
|
||||
if (!dirty.color_mask) {
|
||||
return;
|
||||
}
|
||||
dirty.color_mask = false;
|
||||
|
||||
for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
|
||||
const auto& updated = color_mask[i];
|
||||
auto& current = cur_state.color_mask[i];
|
||||
if (updated.red_enabled != current.red_enabled ||
|
||||
updated.green_enabled != current.green_enabled ||
|
||||
updated.blue_enabled != current.blue_enabled ||
|
||||
updated.alpha_enabled != current.alpha_enabled) {
|
||||
current = updated;
|
||||
glColorMaski(static_cast<GLuint>(i), updated.red_enabled, updated.green_enabled,
|
||||
updated.blue_enabled, updated.alpha_enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyDepth() {
|
||||
Enable(GL_DEPTH_TEST, cur_state.depth.test_enabled, depth.test_enabled);
|
||||
|
||||
if (cur_state.depth.test_func != depth.test_func) {
|
||||
cur_state.depth.test_func = depth.test_func;
|
||||
glDepthFunc(depth.test_func);
|
||||
}
|
||||
|
||||
if (cur_state.depth.write_mask != depth.write_mask) {
|
||||
cur_state.depth.write_mask = depth.write_mask;
|
||||
glDepthMask(depth.write_mask);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyPrimitiveRestart() {
|
||||
Enable(GL_PRIMITIVE_RESTART, cur_state.primitive_restart.enabled, primitive_restart.enabled);
|
||||
|
||||
if (cur_state.primitive_restart.index != primitive_restart.index) {
|
||||
cur_state.primitive_restart.index = primitive_restart.index;
|
||||
glPrimitiveRestartIndex(primitive_restart.index);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyStencilTest() {
|
||||
if (!dirty.stencil_state) {
|
||||
return;
|
||||
}
|
||||
dirty.stencil_state = false;
|
||||
|
||||
Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
|
||||
|
||||
const auto ConfigStencil = [](GLenum face, const auto& config, auto& current) {
|
||||
if (current.test_func != config.test_func || current.test_ref != config.test_ref ||
|
||||
current.test_mask != config.test_mask) {
|
||||
current.test_func = config.test_func;
|
||||
current.test_ref = config.test_ref;
|
||||
current.test_mask = config.test_mask;
|
||||
glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
|
||||
}
|
||||
if (current.action_depth_fail != config.action_depth_fail ||
|
||||
current.action_depth_pass != config.action_depth_pass ||
|
||||
current.action_stencil_fail != config.action_stencil_fail) {
|
||||
current.action_depth_fail = config.action_depth_fail;
|
||||
current.action_depth_pass = config.action_depth_pass;
|
||||
current.action_stencil_fail = config.action_stencil_fail;
|
||||
glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
|
||||
config.action_depth_pass);
|
||||
}
|
||||
if (current.write_mask != config.write_mask) {
|
||||
current.write_mask = config.write_mask;
|
||||
glStencilMaskSeparate(face, config.write_mask);
|
||||
}
|
||||
};
|
||||
ConfigStencil(GL_FRONT, stencil.front, cur_state.stencil.front);
|
||||
ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back);
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyViewport() {
|
||||
for (GLuint i = 0; i < static_cast<GLuint>(Maxwell::NumViewports); ++i) {
|
||||
const auto& updated = viewports[i];
|
||||
auto& current = cur_state.viewports[i];
|
||||
|
||||
if (current.x != updated.x || current.y != updated.y || current.width != updated.width ||
|
||||
current.height != updated.height) {
|
||||
current.x = updated.x;
|
||||
current.y = updated.y;
|
||||
current.width = updated.width;
|
||||
current.height = updated.height;
|
||||
glViewportIndexedf(i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y),
|
||||
static_cast<GLfloat>(updated.width),
|
||||
static_cast<GLfloat>(updated.height));
|
||||
}
|
||||
if (current.depth_range_near != updated.depth_range_near ||
|
||||
current.depth_range_far != updated.depth_range_far) {
|
||||
current.depth_range_near = updated.depth_range_near;
|
||||
current.depth_range_far = updated.depth_range_far;
|
||||
glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
|
||||
}
|
||||
|
||||
Enable(GL_SCISSOR_TEST, i, current.scissor.enabled, updated.scissor.enabled);
|
||||
|
||||
if (current.scissor.x != updated.scissor.x || current.scissor.y != updated.scissor.y ||
|
||||
current.scissor.width != updated.scissor.width ||
|
||||
current.scissor.height != updated.scissor.height) {
|
||||
current.scissor.x = updated.scissor.x;
|
||||
current.scissor.y = updated.scissor.y;
|
||||
current.scissor.width = updated.scissor.width;
|
||||
current.scissor.height = updated.scissor.height;
|
||||
glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
|
||||
updated.scissor.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyGlobalBlending() {
|
||||
const Blend& updated = blend[0];
|
||||
Blend& current = cur_state.blend[0];
|
||||
|
||||
Enable(GL_BLEND, current.enabled, updated.enabled);
|
||||
|
||||
if (current.src_rgb_func != updated.src_rgb_func ||
|
||||
current.dst_rgb_func != updated.dst_rgb_func || current.src_a_func != updated.src_a_func ||
|
||||
current.dst_a_func != updated.dst_a_func) {
|
||||
current.src_rgb_func = updated.src_rgb_func;
|
||||
current.dst_rgb_func = updated.dst_rgb_func;
|
||||
current.src_a_func = updated.src_a_func;
|
||||
current.dst_a_func = updated.dst_a_func;
|
||||
glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
|
||||
updated.dst_a_func);
|
||||
}
|
||||
|
||||
if (current.rgb_equation != updated.rgb_equation || current.a_equation != updated.a_equation) {
|
||||
current.rgb_equation = updated.rgb_equation;
|
||||
current.a_equation = updated.a_equation;
|
||||
glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) {
|
||||
const Blend& updated = blend[target];
|
||||
Blend& current = cur_state.blend[target];
|
||||
|
||||
if (current.enabled != updated.enabled || force) {
|
||||
current.enabled = updated.enabled;
|
||||
Enable(GL_BLEND, static_cast<GLuint>(target), updated.enabled);
|
||||
}
|
||||
|
||||
if (UpdateTie(std::tie(current.src_rgb_func, current.dst_rgb_func, current.src_a_func,
|
||||
current.dst_a_func),
|
||||
std::tie(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
|
||||
updated.dst_a_func))) {
|
||||
glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func,
|
||||
updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
|
||||
}
|
||||
|
||||
if (UpdateTie(std::tie(current.rgb_equation, current.a_equation),
|
||||
std::tie(updated.rgb_equation, updated.a_equation))) {
|
||||
glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation,
|
||||
updated.a_equation);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyBlending() {
|
||||
if (!dirty.blend_state) {
|
||||
return;
|
||||
}
|
||||
dirty.blend_state = false;
|
||||
|
||||
if (independant_blend.enabled) {
|
||||
const bool force = independant_blend.enabled != cur_state.independant_blend.enabled;
|
||||
for (std::size_t target = 0; target < Maxwell::NumRenderTargets; ++target) {
|
||||
ApplyTargetBlending(target, force);
|
||||
}
|
||||
} else {
|
||||
ApplyGlobalBlending();
|
||||
}
|
||||
cur_state.independant_blend.enabled = independant_blend.enabled;
|
||||
|
||||
if (UpdateTie(
|
||||
std::tie(cur_state.blend_color.red, cur_state.blend_color.green,
|
||||
cur_state.blend_color.blue, cur_state.blend_color.alpha),
|
||||
std::tie(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha))) {
|
||||
glBlendColor(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyLogicOp() {
|
||||
Enable(GL_COLOR_LOGIC_OP, cur_state.logic_op.enabled, logic_op.enabled);
|
||||
|
||||
if (UpdateValue(cur_state.logic_op.operation, logic_op.operation)) {
|
||||
glLogicOp(logic_op.operation);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyPolygonOffset() {
|
||||
if (!dirty.polygon_offset) {
|
||||
return;
|
||||
}
|
||||
dirty.polygon_offset = false;
|
||||
|
||||
Enable(GL_POLYGON_OFFSET_FILL, cur_state.polygon_offset.fill_enable,
|
||||
polygon_offset.fill_enable);
|
||||
Enable(GL_POLYGON_OFFSET_LINE, cur_state.polygon_offset.line_enable,
|
||||
polygon_offset.line_enable);
|
||||
Enable(GL_POLYGON_OFFSET_POINT, cur_state.polygon_offset.point_enable,
|
||||
polygon_offset.point_enable);
|
||||
|
||||
if (UpdateTie(std::tie(cur_state.polygon_offset.factor, cur_state.polygon_offset.units,
|
||||
cur_state.polygon_offset.clamp),
|
||||
std::tie(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp))) {
|
||||
if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) {
|
||||
glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp);
|
||||
} else {
|
||||
UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0,
|
||||
"Unimplemented Depth polygon offset clamp.");
|
||||
glPolygonOffset(polygon_offset.factor, polygon_offset.units);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyAlphaTest() {
|
||||
Enable(GL_ALPHA_TEST, cur_state.alpha_test.enabled, alpha_test.enabled);
|
||||
if (UpdateTie(std::tie(cur_state.alpha_test.func, cur_state.alpha_test.ref),
|
||||
std::tie(alpha_test.func, alpha_test.ref))) {
|
||||
glAlphaFunc(alpha_test.func, alpha_test.ref);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyClipControl() {
|
||||
if (UpdateTie(std::tie(cur_state.clip_control.origin, cur_state.clip_control.depth_mode),
|
||||
std::tie(clip_control.origin, clip_control.depth_mode))) {
|
||||
glClipControl(clip_control.origin, clip_control.depth_mode);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyRenderBuffer() {
|
||||
if (cur_state.renderbuffer != renderbuffer) {
|
||||
cur_state.renderbuffer = renderbuffer;
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyTextures() {
|
||||
const std::size_t size = std::size(textures);
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
if (UpdateValue(cur_state.textures[i], textures[i])) {
|
||||
// BindTextureUnit doesn't support binding null textures, skip those binds.
|
||||
// TODO(Rodrigo): Stop using null textures
|
||||
if (textures[i] != 0) {
|
||||
glBindTextureUnit(static_cast<GLuint>(i), textures[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplySamplers() {
|
||||
const std::size_t size = std::size(samplers);
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
if (UpdateValue(cur_state.samplers[i], samplers[i])) {
|
||||
glBindSampler(static_cast<GLuint>(i), samplers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyImages() {
|
||||
if (const auto update = UpdateArray(cur_state.images, images)) {
|
||||
glBindImageTextures(update->first, update->second, images.data() + update->first);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::Apply() {
|
||||
MICROPROFILE_SCOPE(OpenGL_State);
|
||||
ApplyFramebufferState();
|
||||
ApplyVertexArrayState();
|
||||
ApplyShaderProgram();
|
||||
ApplyProgramPipeline();
|
||||
ApplyClipDistances();
|
||||
ApplyPointSize();
|
||||
ApplyFragmentColorClamp();
|
||||
ApplyMultisample();
|
||||
ApplyRasterizerDiscard();
|
||||
ApplyColorMask();
|
||||
ApplyDepthClamp();
|
||||
ApplyViewport();
|
||||
ApplyStencilTest();
|
||||
ApplySRgb();
|
||||
ApplyCulling();
|
||||
ApplyDepth();
|
||||
ApplyPrimitiveRestart();
|
||||
ApplyBlending();
|
||||
ApplyLogicOp();
|
||||
ApplyTextures();
|
||||
ApplySamplers();
|
||||
ApplyImages();
|
||||
ApplyPolygonOffset();
|
||||
ApplyAlphaTest();
|
||||
ApplyClipControl();
|
||||
ApplyRenderBuffer();
|
||||
}
|
||||
|
||||
void OpenGLState::EmulateViewportWithScissor() {
|
||||
auto& current = viewports[0];
|
||||
if (current.scissor.enabled) {
|
||||
const GLint left = std::max(current.x, current.scissor.x);
|
||||
const GLint right =
|
||||
std::max(current.x + current.width, current.scissor.x + current.scissor.width);
|
||||
const GLint bottom = std::max(current.y, current.scissor.y);
|
||||
const GLint top =
|
||||
std::max(current.y + current.height, current.scissor.y + current.scissor.height);
|
||||
current.scissor.x = std::max(left, 0);
|
||||
current.scissor.y = std::max(bottom, 0);
|
||||
current.scissor.width = std::max(right - left, 0);
|
||||
current.scissor.height = std::max(top - bottom, 0);
|
||||
} else {
|
||||
current.scissor.enabled = true;
|
||||
current.scissor.x = current.x;
|
||||
current.scissor.y = current.y;
|
||||
current.scissor.width = current.width;
|
||||
current.scissor.height = current.height;
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
|
||||
for (auto& texture : textures) {
|
||||
if (texture == handle) {
|
||||
texture = 0;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
|
||||
for (auto& sampler : samplers) {
|
||||
if (sampler == handle) {
|
||||
sampler = 0;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
|
||||
if (draw.shader_program == handle) {
|
||||
draw.shader_program = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
|
||||
if (draw.program_pipeline == handle) {
|
||||
draw.program_pipeline = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
|
||||
if (draw.vertex_array == handle) {
|
||||
draw.vertex_array = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
|
||||
if (draw.read_framebuffer == handle) {
|
||||
draw.read_framebuffer = 0;
|
||||
}
|
||||
if (draw.draw_framebuffer == handle) {
|
||||
draw.draw_framebuffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
OpenGLState& OpenGLState::ResetRenderbuffer(GLuint handle) {
|
||||
if (renderbuffer == handle) {
|
||||
renderbuffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
@ -1,251 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <glad/glad.h>
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class OpenGLState {
|
||||
public:
|
||||
struct {
|
||||
bool enabled = false; // GL_FRAMEBUFFER_SRGB
|
||||
} framebuffer_srgb;
|
||||
|
||||
struct {
|
||||
bool alpha_to_coverage = false; // GL_ALPHA_TO_COVERAGE
|
||||
bool alpha_to_one = false; // GL_ALPHA_TO_ONE
|
||||
} multisample_control;
|
||||
|
||||
struct {
|
||||
bool enabled = false; // GL_CLAMP_FRAGMENT_COLOR_ARB
|
||||
} fragment_color_clamp;
|
||||
|
||||
struct {
|
||||
bool far_plane = false;
|
||||
bool near_plane = false;
|
||||
} depth_clamp; // GL_DEPTH_CLAMP
|
||||
|
||||
struct {
|
||||
bool enabled = false; // GL_CULL_FACE
|
||||
GLenum mode = GL_BACK; // GL_CULL_FACE_MODE
|
||||
GLenum front_face = GL_CCW; // GL_FRONT_FACE
|
||||
} cull;
|
||||
|
||||
struct {
|
||||
bool test_enabled = false; // GL_DEPTH_TEST
|
||||
GLboolean write_mask = GL_TRUE; // GL_DEPTH_WRITEMASK
|
||||
GLenum test_func = GL_LESS; // GL_DEPTH_FUNC
|
||||
} depth;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
GLuint index = 0;
|
||||
} primitive_restart; // GL_PRIMITIVE_RESTART
|
||||
|
||||
bool rasterizer_discard = false; // GL_RASTERIZER_DISCARD
|
||||
|
||||
struct ColorMask {
|
||||
GLboolean red_enabled = GL_TRUE;
|
||||
GLboolean green_enabled = GL_TRUE;
|
||||
GLboolean blue_enabled = GL_TRUE;
|
||||
GLboolean alpha_enabled = GL_TRUE;
|
||||
};
|
||||
std::array<ColorMask, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets>
|
||||
color_mask; // GL_COLOR_WRITEMASK
|
||||
|
||||
struct {
|
||||
bool test_enabled = false; // GL_STENCIL_TEST
|
||||
struct {
|
||||
GLenum test_func = GL_ALWAYS; // GL_STENCIL_FUNC
|
||||
GLint test_ref = 0; // GL_STENCIL_REF
|
||||
GLuint test_mask = 0xFFFFFFFF; // GL_STENCIL_VALUE_MASK
|
||||
GLuint write_mask = 0xFFFFFFFF; // GL_STENCIL_WRITEMASK
|
||||
GLenum action_stencil_fail = GL_KEEP; // GL_STENCIL_FAIL
|
||||
GLenum action_depth_fail = GL_KEEP; // GL_STENCIL_PASS_DEPTH_FAIL
|
||||
GLenum action_depth_pass = GL_KEEP; // GL_STENCIL_PASS_DEPTH_PASS
|
||||
} front, back;
|
||||
} stencil;
|
||||
|
||||
struct Blend {
|
||||
bool enabled = false; // GL_BLEND
|
||||
GLenum rgb_equation = GL_FUNC_ADD; // GL_BLEND_EQUATION_RGB
|
||||
GLenum a_equation = GL_FUNC_ADD; // GL_BLEND_EQUATION_ALPHA
|
||||
GLenum src_rgb_func = GL_ONE; // GL_BLEND_SRC_RGB
|
||||
GLenum dst_rgb_func = GL_ZERO; // GL_BLEND_DST_RGB
|
||||
GLenum src_a_func = GL_ONE; // GL_BLEND_SRC_ALPHA
|
||||
GLenum dst_a_func = GL_ZERO; // GL_BLEND_DST_ALPHA
|
||||
};
|
||||
std::array<Blend, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> blend;
|
||||
|
||||
struct {
|
||||
bool enabled = false;
|
||||
} independant_blend;
|
||||
|
||||
struct {
|
||||
GLclampf red = 0.0f;
|
||||
GLclampf green = 0.0f;
|
||||
GLclampf blue = 0.0f;
|
||||
GLclampf alpha = 0.0f;
|
||||
} blend_color; // GL_BLEND_COLOR
|
||||
|
||||
struct {
|
||||
bool enabled = false; // GL_LOGIC_OP_MODE
|
||||
GLenum operation = GL_COPY;
|
||||
} logic_op;
|
||||
|
||||
static constexpr std::size_t NumSamplers = 32 * 5;
|
||||
static constexpr std::size_t NumImages = 8 * 5;
|
||||
std::array<GLuint, NumSamplers> textures = {};
|
||||
std::array<GLuint, NumSamplers> samplers = {};
|
||||
std::array<GLuint, NumImages> images = {};
|
||||
|
||||
struct {
|
||||
GLuint read_framebuffer = 0; // GL_READ_FRAMEBUFFER_BINDING
|
||||
GLuint draw_framebuffer = 0; // GL_DRAW_FRAMEBUFFER_BINDING
|
||||
GLuint vertex_array = 0; // GL_VERTEX_ARRAY_BINDING
|
||||
GLuint shader_program = 0; // GL_CURRENT_PROGRAM
|
||||
GLuint program_pipeline = 0; // GL_PROGRAM_PIPELINE_BINDING
|
||||
} draw;
|
||||
|
||||
struct Viewport {
|
||||
GLint x = 0;
|
||||
GLint y = 0;
|
||||
GLint width = 0;
|
||||
GLint height = 0;
|
||||
GLfloat depth_range_near = 0.0f; // GL_DEPTH_RANGE
|
||||
GLfloat depth_range_far = 1.0f; // GL_DEPTH_RANGE
|
||||
struct {
|
||||
bool enabled = false; // GL_SCISSOR_TEST
|
||||
GLint x = 0;
|
||||
GLint y = 0;
|
||||
GLsizei width = 0;
|
||||
GLsizei height = 0;
|
||||
} scissor;
|
||||
};
|
||||
std::array<Viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
|
||||
|
||||
struct {
|
||||
bool program_control = false; // GL_PROGRAM_POINT_SIZE
|
||||
bool sprite = false; // GL_POINT_SPRITE
|
||||
GLfloat size = 1.0f; // GL_POINT_SIZE
|
||||
} point;
|
||||
|
||||
struct {
|
||||
bool point_enable = false;
|
||||
bool line_enable = false;
|
||||
bool fill_enable = false;
|
||||
GLfloat units = 0.0f;
|
||||
GLfloat factor = 0.0f;
|
||||
GLfloat clamp = 0.0f;
|
||||
} polygon_offset;
|
||||
|
||||
struct {
|
||||
bool enabled = false; // GL_ALPHA_TEST
|
||||
GLenum func = GL_ALWAYS; // GL_ALPHA_TEST_FUNC
|
||||
GLfloat ref = 0.0f; // GL_ALPHA_TEST_REF
|
||||
} alpha_test;
|
||||
|
||||
std::array<bool, 8> clip_distance = {}; // GL_CLIP_DISTANCE
|
||||
|
||||
struct {
|
||||
GLenum origin = GL_LOWER_LEFT;
|
||||
GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE;
|
||||
} clip_control;
|
||||
|
||||
GLuint renderbuffer{}; // GL_RENDERBUFFER_BINDING
|
||||
|
||||
OpenGLState();
|
||||
|
||||
/// Get the currently active OpenGL state
|
||||
static OpenGLState GetCurState() {
|
||||
return cur_state;
|
||||
}
|
||||
|
||||
void SetDefaultViewports();
|
||||
/// Apply this state as the current OpenGL state
|
||||
void Apply();
|
||||
|
||||
void ApplyFramebufferState();
|
||||
void ApplyVertexArrayState();
|
||||
void ApplyShaderProgram();
|
||||
void ApplyProgramPipeline();
|
||||
void ApplyClipDistances();
|
||||
void ApplyPointSize();
|
||||
void ApplyFragmentColorClamp();
|
||||
void ApplyMultisample();
|
||||
void ApplySRgb();
|
||||
void ApplyCulling();
|
||||
void ApplyRasterizerDiscard();
|
||||
void ApplyColorMask();
|
||||
void ApplyDepth();
|
||||
void ApplyPrimitiveRestart();
|
||||
void ApplyStencilTest();
|
||||
void ApplyViewport();
|
||||
void ApplyTargetBlending(std::size_t target, bool force);
|
||||
void ApplyGlobalBlending();
|
||||
void ApplyBlending();
|
||||
void ApplyLogicOp();
|
||||
void ApplyTextures();
|
||||
void ApplySamplers();
|
||||
void ApplyImages();
|
||||
void ApplyDepthClamp();
|
||||
void ApplyPolygonOffset();
|
||||
void ApplyAlphaTest();
|
||||
void ApplyClipControl();
|
||||
void ApplyRenderBuffer();
|
||||
|
||||
/// Resets any references to the given resource
|
||||
OpenGLState& UnbindTexture(GLuint handle);
|
||||
OpenGLState& ResetSampler(GLuint handle);
|
||||
OpenGLState& ResetProgram(GLuint handle);
|
||||
OpenGLState& ResetPipeline(GLuint handle);
|
||||
OpenGLState& ResetVertexArray(GLuint handle);
|
||||
OpenGLState& ResetFramebuffer(GLuint handle);
|
||||
OpenGLState& ResetRenderbuffer(GLuint handle);
|
||||
|
||||
/// Viewport does not affects glClearBuffer so emulate viewport using scissor test
|
||||
void EmulateViewportWithScissor();
|
||||
|
||||
void MarkDirtyBlendState() {
|
||||
dirty.blend_state = true;
|
||||
}
|
||||
|
||||
void MarkDirtyStencilState() {
|
||||
dirty.stencil_state = true;
|
||||
}
|
||||
|
||||
void MarkDirtyPolygonOffset() {
|
||||
dirty.polygon_offset = true;
|
||||
}
|
||||
|
||||
void MarkDirtyColorMask() {
|
||||
dirty.color_mask = true;
|
||||
}
|
||||
|
||||
void AllDirty() {
|
||||
dirty.blend_state = true;
|
||||
dirty.stencil_state = true;
|
||||
dirty.polygon_offset = true;
|
||||
dirty.color_mask = true;
|
||||
}
|
||||
|
||||
private:
|
||||
static OpenGLState cur_state;
|
||||
|
||||
struct {
|
||||
bool blend_state;
|
||||
bool stencil_state;
|
||||
bool viewport_state;
|
||||
bool polygon_offset;
|
||||
bool color_mask;
|
||||
} dirty{};
|
||||
};
|
||||
static_assert(std::is_trivially_copyable_v<OpenGLState>);
|
||||
|
||||
} // namespace OpenGL
|
238
src/video_core/renderer_opengl/gl_state_tracker.cpp
Normal file
238
src/video_core/renderer_opengl/gl_state_tracker.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
|
||||
#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
|
||||
#define NUM(field_name) (sizeof(Maxwell3D::Regs::field_name) / sizeof(u32))
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace Dirty;
|
||||
using namespace VideoCommon::Dirty;
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
using Regs = Maxwell3D::Regs;
|
||||
using Tables = Maxwell3D::DirtyState::Tables;
|
||||
using Table = Maxwell3D::DirtyState::Table;
|
||||
|
||||
void SetupDirtyColorMasks(Tables& tables) {
|
||||
tables[0][OFF(color_mask_common)] = ColorMaskCommon;
|
||||
for (std::size_t rt = 0; rt < Regs::NumRenderTargets; ++rt) {
|
||||
const std::size_t offset = OFF(color_mask) + rt * NUM(color_mask[0]);
|
||||
FillBlock(tables[0], offset, NUM(color_mask[0]), ColorMask0 + rt);
|
||||
}
|
||||
|
||||
FillBlock(tables[1], OFF(color_mask), NUM(color_mask), ColorMasks);
|
||||
}
|
||||
|
||||
void SetupDirtyVertexArrays(Tables& tables) {
|
||||
static constexpr std::size_t num_array = 3;
|
||||
static constexpr std::size_t instance_base_offset = 3;
|
||||
for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
|
||||
const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]);
|
||||
const std::size_t limit_offset = OFF(vertex_array_limit) + i * NUM(vertex_array_limit[0]);
|
||||
|
||||
FillBlock(tables, array_offset, num_array, VertexBuffer0 + i, VertexBuffers);
|
||||
FillBlock(tables, limit_offset, NUM(vertex_array_limit), VertexBuffer0 + i, VertexBuffers);
|
||||
|
||||
const std::size_t instance_array_offset = array_offset + instance_base_offset;
|
||||
tables[0][instance_array_offset] = static_cast<u8>(VertexInstance0 + i);
|
||||
tables[1][instance_array_offset] = VertexInstances;
|
||||
|
||||
const std::size_t instance_offset = OFF(instanced_arrays) + i;
|
||||
tables[0][instance_offset] = static_cast<u8>(VertexInstance0 + i);
|
||||
tables[1][instance_offset] = VertexInstances;
|
||||
}
|
||||
}
|
||||
|
||||
void SetupDirtyVertexFormat(Tables& tables) {
|
||||
for (std::size_t i = 0; i < Regs::NumVertexAttributes; ++i) {
|
||||
const std::size_t offset = OFF(vertex_attrib_format) + i * NUM(vertex_attrib_format[0]);
|
||||
FillBlock(tables[0], offset, NUM(vertex_attrib_format[0]), VertexFormat0 + i);
|
||||
}
|
||||
|
||||
FillBlock(tables[1], OFF(vertex_attrib_format), Regs::NumVertexAttributes, VertexFormats);
|
||||
}
|
||||
|
||||
void SetupDirtyViewports(Tables& tables) {
|
||||
for (std::size_t i = 0; i < Regs::NumViewports; ++i) {
|
||||
const std::size_t transf_offset = OFF(viewport_transform) + i * NUM(viewport_transform[0]);
|
||||
const std::size_t viewport_offset = OFF(viewports) + i * NUM(viewports[0]);
|
||||
|
||||
FillBlock(tables[0], transf_offset, NUM(viewport_transform[0]), Viewport0 + i);
|
||||
FillBlock(tables[0], viewport_offset, NUM(viewports[0]), Viewport0 + i);
|
||||
}
|
||||
|
||||
FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);
|
||||
FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports);
|
||||
|
||||
tables[0][OFF(viewport_transform_enabled)] = ViewportTransform;
|
||||
tables[1][OFF(viewport_transform_enabled)] = Viewports;
|
||||
}
|
||||
|
||||
void SetupDirtyScissors(Tables& tables) {
|
||||
for (std::size_t i = 0; i < Regs::NumViewports; ++i) {
|
||||
const std::size_t offset = OFF(scissor_test) + i * NUM(scissor_test[0]);
|
||||
FillBlock(tables[0], offset, NUM(scissor_test[0]), Scissor0 + i);
|
||||
}
|
||||
FillBlock(tables[1], OFF(scissor_test), NUM(scissor_test), Scissors);
|
||||
}
|
||||
|
||||
void SetupDirtyShaders(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(shader_config[0]), NUM(shader_config[0]) * Regs::MaxShaderProgram,
|
||||
Shaders);
|
||||
}
|
||||
|
||||
void SetupDirtyDepthTest(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(depth_test_enable)] = DepthTest;
|
||||
table[OFF(depth_write_enabled)] = DepthMask;
|
||||
table[OFF(depth_test_func)] = DepthTest;
|
||||
}
|
||||
|
||||
void SetupDirtyStencilTest(Tables& tables) {
|
||||
static constexpr std::array offsets = {
|
||||
OFF(stencil_enable), OFF(stencil_front_func_func), OFF(stencil_front_func_ref),
|
||||
OFF(stencil_front_func_mask), OFF(stencil_front_op_fail), OFF(stencil_front_op_zfail),
|
||||
OFF(stencil_front_op_zpass), OFF(stencil_front_mask), OFF(stencil_two_side_enable),
|
||||
OFF(stencil_back_func_func), OFF(stencil_back_func_ref), OFF(stencil_back_func_mask),
|
||||
OFF(stencil_back_op_fail), OFF(stencil_back_op_zfail), OFF(stencil_back_op_zpass),
|
||||
OFF(stencil_back_mask)};
|
||||
for (const auto offset : offsets) {
|
||||
tables[0][offset] = StencilTest;
|
||||
}
|
||||
}
|
||||
|
||||
void SetupDirtyAlphaTest(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(alpha_test_ref)] = AlphaTest;
|
||||
table[OFF(alpha_test_func)] = AlphaTest;
|
||||
table[OFF(alpha_test_enabled)] = AlphaTest;
|
||||
}
|
||||
|
||||
void SetupDirtyBlend(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor);
|
||||
|
||||
tables[0][OFF(independent_blend_enable)] = BlendIndependentEnabled;
|
||||
|
||||
for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
|
||||
const std::size_t offset = OFF(independent_blend) + i * NUM(independent_blend[0]);
|
||||
FillBlock(tables[0], offset, NUM(independent_blend[0]), BlendState0 + i);
|
||||
|
||||
tables[0][OFF(blend.enable) + i] = static_cast<u8>(BlendState0 + i);
|
||||
}
|
||||
FillBlock(tables[1], OFF(independent_blend), NUM(independent_blend), BlendStates);
|
||||
FillBlock(tables[1], OFF(blend), NUM(blend), BlendStates);
|
||||
}
|
||||
|
||||
void SetupDirtyPrimitiveRestart(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(primitive_restart), NUM(primitive_restart), PrimitiveRestart);
|
||||
}
|
||||
|
||||
void SetupDirtyPolygonOffset(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(polygon_offset_fill_enable)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_line_enable)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_point_enable)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_factor)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_units)] = PolygonOffset;
|
||||
table[OFF(polygon_offset_clamp)] = PolygonOffset;
|
||||
}
|
||||
|
||||
void SetupDirtyMultisampleControl(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(multisample_control), NUM(multisample_control), MultisampleControl);
|
||||
}
|
||||
|
||||
void SetupDirtyRasterizeEnable(Tables& tables) {
|
||||
tables[0][OFF(rasterize_enable)] = RasterizeEnable;
|
||||
}
|
||||
|
||||
void SetupDirtyFramebufferSRGB(Tables& tables) {
|
||||
tables[0][OFF(framebuffer_srgb)] = FramebufferSRGB;
|
||||
}
|
||||
|
||||
void SetupDirtyLogicOp(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(logic_op), NUM(logic_op), LogicOp);
|
||||
}
|
||||
|
||||
void SetupDirtyFragmentClampColor(Tables& tables) {
|
||||
tables[0][OFF(frag_color_clamp)] = FragmentClampColor;
|
||||
}
|
||||
|
||||
void SetupDirtyPointSize(Tables& tables) {
|
||||
tables[0][OFF(vp_point_size)] = PointSize;
|
||||
tables[0][OFF(point_size)] = PointSize;
|
||||
tables[0][OFF(point_sprite_enable)] = PointSize;
|
||||
}
|
||||
|
||||
void SetupDirtyClipControl(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(screen_y_control)] = ClipControl;
|
||||
table[OFF(depth_mode)] = ClipControl;
|
||||
}
|
||||
|
||||
void SetupDirtyDepthClampEnabled(Tables& tables) {
|
||||
tables[0][OFF(view_volume_clip_control)] = DepthClampEnabled;
|
||||
}
|
||||
|
||||
void SetupDirtyMisc(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
|
||||
table[OFF(clip_distance_enabled)] = ClipDistances;
|
||||
|
||||
table[OFF(front_face)] = FrontFace;
|
||||
|
||||
table[OFF(cull_test_enabled)] = CullTest;
|
||||
table[OFF(cull_face)] = CullTest;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
StateTracker::StateTracker(Core::System& system) : system{system} {}
|
||||
|
||||
void StateTracker::Initialize() {
|
||||
auto& dirty = system.GPU().Maxwell3D().dirty;
|
||||
auto& tables = dirty.tables;
|
||||
SetupDirtyRenderTargets(tables);
|
||||
SetupDirtyColorMasks(tables);
|
||||
SetupDirtyViewports(tables);
|
||||
SetupDirtyScissors(tables);
|
||||
SetupDirtyVertexArrays(tables);
|
||||
SetupDirtyVertexFormat(tables);
|
||||
SetupDirtyShaders(tables);
|
||||
SetupDirtyDepthTest(tables);
|
||||
SetupDirtyStencilTest(tables);
|
||||
SetupDirtyAlphaTest(tables);
|
||||
SetupDirtyBlend(tables);
|
||||
SetupDirtyPrimitiveRestart(tables);
|
||||
SetupDirtyPolygonOffset(tables);
|
||||
SetupDirtyMultisampleControl(tables);
|
||||
SetupDirtyRasterizeEnable(tables);
|
||||
SetupDirtyFramebufferSRGB(tables);
|
||||
SetupDirtyLogicOp(tables);
|
||||
SetupDirtyFragmentClampColor(tables);
|
||||
SetupDirtyPointSize(tables);
|
||||
SetupDirtyClipControl(tables);
|
||||
SetupDirtyDepthClampEnabled(tables);
|
||||
SetupDirtyMisc(tables);
|
||||
|
||||
auto& store = dirty.on_write_stores;
|
||||
SetupCommonOnWriteStores(store);
|
||||
store[VertexBuffers] = true;
|
||||
for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
|
||||
store[VertexBuffer0 + i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
204
src/video_core/renderer_opengl/gl_state_tracker.h
Normal file
204
src/video_core/renderer_opengl/gl_state_tracker.h
Normal file
@ -0,0 +1,204 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
namespace Dirty {
|
||||
|
||||
enum : u8 {
|
||||
First = VideoCommon::Dirty::LastCommonEntry,
|
||||
|
||||
VertexFormats,
|
||||
VertexFormat0,
|
||||
VertexFormat31 = VertexFormat0 + 31,
|
||||
|
||||
VertexBuffers,
|
||||
VertexBuffer0,
|
||||
VertexBuffer31 = VertexBuffer0 + 31,
|
||||
|
||||
VertexInstances,
|
||||
VertexInstance0,
|
||||
VertexInstance31 = VertexInstance0 + 31,
|
||||
|
||||
ViewportTransform,
|
||||
Viewports,
|
||||
Viewport0,
|
||||
Viewport15 = Viewport0 + 15,
|
||||
|
||||
Scissors,
|
||||
Scissor0,
|
||||
Scissor15 = Scissor0 + 15,
|
||||
|
||||
ColorMaskCommon,
|
||||
ColorMasks,
|
||||
ColorMask0,
|
||||
ColorMask7 = ColorMask0 + 7,
|
||||
|
||||
BlendColor,
|
||||
BlendIndependentEnabled,
|
||||
BlendStates,
|
||||
BlendState0,
|
||||
BlendState7 = BlendState0 + 7,
|
||||
|
||||
Shaders,
|
||||
ClipDistances,
|
||||
|
||||
ColorMask,
|
||||
FrontFace,
|
||||
CullTest,
|
||||
DepthMask,
|
||||
DepthTest,
|
||||
StencilTest,
|
||||
AlphaTest,
|
||||
PrimitiveRestart,
|
||||
PolygonOffset,
|
||||
MultisampleControl,
|
||||
RasterizeEnable,
|
||||
FramebufferSRGB,
|
||||
LogicOp,
|
||||
FragmentClampColor,
|
||||
PointSize,
|
||||
ClipControl,
|
||||
DepthClampEnabled,
|
||||
|
||||
Last
|
||||
};
|
||||
static_assert(Last <= std::numeric_limits<u8>::max());
|
||||
|
||||
} // namespace Dirty
|
||||
|
||||
class StateTracker {
|
||||
public:
|
||||
explicit StateTracker(Core::System& system);
|
||||
|
||||
void Initialize();
|
||||
|
||||
void BindIndexBuffer(GLuint new_index_buffer) {
|
||||
if (index_buffer == new_index_buffer) {
|
||||
return;
|
||||
}
|
||||
index_buffer = new_index_buffer;
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, new_index_buffer);
|
||||
}
|
||||
|
||||
void NotifyScreenDrawVertexArray() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::VertexFormats] = true;
|
||||
flags[OpenGL::Dirty::VertexFormat0 + 0] = true;
|
||||
flags[OpenGL::Dirty::VertexFormat0 + 1] = true;
|
||||
|
||||
flags[OpenGL::Dirty::VertexBuffers] = true;
|
||||
flags[OpenGL::Dirty::VertexBuffer0] = true;
|
||||
|
||||
flags[OpenGL::Dirty::VertexInstances] = true;
|
||||
flags[OpenGL::Dirty::VertexInstance0 + 0] = true;
|
||||
flags[OpenGL::Dirty::VertexInstance0 + 1] = true;
|
||||
}
|
||||
|
||||
void NotifyViewport0() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::Viewports] = true;
|
||||
flags[OpenGL::Dirty::Viewport0] = true;
|
||||
}
|
||||
|
||||
void NotifyScissor0() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::Scissors] = true;
|
||||
flags[OpenGL::Dirty::Scissor0] = true;
|
||||
}
|
||||
|
||||
void NotifyColorMask0() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::ColorMasks] = true;
|
||||
flags[OpenGL::Dirty::ColorMask0] = true;
|
||||
}
|
||||
|
||||
void NotifyBlend0() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::BlendStates] = true;
|
||||
flags[OpenGL::Dirty::BlendState0] = true;
|
||||
}
|
||||
|
||||
void NotifyFramebuffer() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[VideoCommon::Dirty::RenderTargets] = true;
|
||||
}
|
||||
|
||||
void NotifyFrontFace() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::FrontFace] = true;
|
||||
}
|
||||
|
||||
void NotifyCullTest() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::CullTest] = true;
|
||||
}
|
||||
|
||||
void NotifyDepthMask() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::DepthMask] = true;
|
||||
}
|
||||
|
||||
void NotifyDepthTest() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::DepthTest] = true;
|
||||
}
|
||||
|
||||
void NotifyStencilTest() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::StencilTest] = true;
|
||||
}
|
||||
|
||||
void NotifyPolygonOffset() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::PolygonOffset] = true;
|
||||
}
|
||||
|
||||
void NotifyRasterizeEnable() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::RasterizeEnable] = true;
|
||||
}
|
||||
|
||||
void NotifyFramebufferSRGB() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::FramebufferSRGB] = true;
|
||||
}
|
||||
|
||||
void NotifyLogicOp() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::LogicOp] = true;
|
||||
}
|
||||
|
||||
void NotifyClipControl() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::ClipControl] = true;
|
||||
}
|
||||
|
||||
void NotifyAlphaTest() {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
flags[OpenGL::Dirty::AlphaTest] = true;
|
||||
}
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
GLuint index_buffer = 0;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
@ -7,7 +7,6 @@
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "core/core.h"
|
||||
#include "video_core/morton.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
#include "video_core/texture_cache/surface_base.h"
|
||||
@ -397,6 +397,7 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p
|
||||
const bool is_proxy)
|
||||
: VideoCommon::ViewBase(params), surface{surface}, is_proxy{is_proxy} {
|
||||
target = GetTextureTarget(params.target);
|
||||
format = GetFormatTuple(surface.GetSurfaceParams().pixel_format).internal_format;
|
||||
if (!is_proxy) {
|
||||
texture_view = CreateTextureView();
|
||||
}
|
||||
@ -467,25 +468,20 @@ void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_sou
|
||||
}
|
||||
|
||||
OGLTextureView CachedSurfaceView::CreateTextureView() const {
|
||||
const auto& owner_params = surface.GetSurfaceParams();
|
||||
OGLTextureView texture_view;
|
||||
texture_view.Create();
|
||||
|
||||
const GLuint handle{texture_view.handle};
|
||||
const FormatTuple& tuple{GetFormatTuple(owner_params.pixel_format)};
|
||||
|
||||
glTextureView(handle, target, surface.texture.handle, tuple.internal_format, params.base_level,
|
||||
glTextureView(texture_view.handle, target, surface.texture.handle, format, params.base_level,
|
||||
params.num_levels, params.base_layer, params.num_layers);
|
||||
|
||||
ApplyTextureDefaults(owner_params, handle);
|
||||
ApplyTextureDefaults(surface.GetSurfaceParams(), texture_view.handle);
|
||||
|
||||
return texture_view;
|
||||
}
|
||||
|
||||
TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
|
||||
VideoCore::RasterizerInterface& rasterizer,
|
||||
const Device& device)
|
||||
: TextureCacheBase{system, rasterizer} {
|
||||
const Device& device, StateTracker& state_tracker)
|
||||
: TextureCacheBase{system, rasterizer}, state_tracker{state_tracker} {
|
||||
src_framebuffer.Create();
|
||||
dst_framebuffer.Create();
|
||||
}
|
||||
@ -519,25 +515,26 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
|
||||
const Tegra::Engines::Fermi2D::Config& copy_config) {
|
||||
const auto& src_params{src_view->GetSurfaceParams()};
|
||||
const auto& dst_params{dst_view->GetSurfaceParams()};
|
||||
|
||||
OpenGLState prev_state{OpenGLState::GetCurState()};
|
||||
SCOPE_EXIT({
|
||||
prev_state.AllDirty();
|
||||
prev_state.Apply();
|
||||
});
|
||||
|
||||
OpenGLState state;
|
||||
state.draw.read_framebuffer = src_framebuffer.handle;
|
||||
state.draw.draw_framebuffer = dst_framebuffer.handle;
|
||||
state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
|
||||
u32 buffers{};
|
||||
|
||||
UNIMPLEMENTED_IF(src_params.target == SurfaceTarget::Texture3D);
|
||||
UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D);
|
||||
|
||||
state_tracker.NotifyScissor0();
|
||||
state_tracker.NotifyFramebuffer();
|
||||
state_tracker.NotifyRasterizeEnable();
|
||||
state_tracker.NotifyFramebufferSRGB();
|
||||
|
||||
if (dst_params.srgb_conversion) {
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
} else {
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
glDisable(GL_RASTERIZER_DISCARD);
|
||||
glDisablei(GL_SCISSOR_TEST, 0);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, src_framebuffer.handle);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer.handle);
|
||||
|
||||
GLenum buffers = 0;
|
||||
if (src_params.type == SurfaceType::ColorTexture) {
|
||||
src_view->Attach(GL_COLOR_ATTACHMENT0, GL_READ_FRAMEBUFFER);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
|
||||
|
@ -27,6 +27,7 @@ using VideoCommon::ViewParams;
|
||||
class CachedSurfaceView;
|
||||
class CachedSurface;
|
||||
class TextureCacheOpenGL;
|
||||
class StateTracker;
|
||||
|
||||
using Surface = std::shared_ptr<CachedSurface>;
|
||||
using View = std::shared_ptr<CachedSurfaceView>;
|
||||
@ -96,6 +97,10 @@ public:
|
||||
return texture_view.handle;
|
||||
}
|
||||
|
||||
GLenum GetFormat() const {
|
||||
return format;
|
||||
}
|
||||
|
||||
const SurfaceParams& GetSurfaceParams() const {
|
||||
return surface.GetSurfaceParams();
|
||||
}
|
||||
@ -113,6 +118,7 @@ private:
|
||||
|
||||
CachedSurface& surface;
|
||||
GLenum target{};
|
||||
GLenum format{};
|
||||
|
||||
OGLTextureView texture_view;
|
||||
u32 swizzle{};
|
||||
@ -122,7 +128,7 @@ private:
|
||||
class TextureCacheOpenGL final : public TextureCacheBase {
|
||||
public:
|
||||
explicit TextureCacheOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||
const Device& device);
|
||||
const Device& device, StateTracker& state_tracker);
|
||||
~TextureCacheOpenGL();
|
||||
|
||||
protected:
|
||||
@ -139,6 +145,8 @@ protected:
|
||||
private:
|
||||
GLuint FetchPBO(std::size_t buffer_size);
|
||||
|
||||
StateTracker& state_tracker;
|
||||
|
||||
OGLFramebuffer src_framebuffer;
|
||||
OGLFramebuffer dst_framebuffer;
|
||||
std::unordered_map<u32, OGLBuffer> copy_pbo_cache;
|
||||
|
@ -425,24 +425,24 @@ inline GLenum StencilOp(Maxwell::StencilOp stencil) {
|
||||
return GL_KEEP;
|
||||
}
|
||||
|
||||
inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
|
||||
inline GLenum FrontFace(Maxwell::FrontFace front_face) {
|
||||
switch (front_face) {
|
||||
case Maxwell::Cull::FrontFace::ClockWise:
|
||||
case Maxwell::FrontFace::ClockWise:
|
||||
return GL_CW;
|
||||
case Maxwell::Cull::FrontFace::CounterClockWise:
|
||||
case Maxwell::FrontFace::CounterClockWise:
|
||||
return GL_CCW;
|
||||
}
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
|
||||
return GL_CCW;
|
||||
}
|
||||
|
||||
inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
|
||||
inline GLenum CullFace(Maxwell::CullFace cull_face) {
|
||||
switch (cull_face) {
|
||||
case Maxwell::Cull::CullFace::Front:
|
||||
case Maxwell::CullFace::Front:
|
||||
return GL_FRONT;
|
||||
case Maxwell::Cull::CullFace::Back:
|
||||
case Maxwell::CullFace::Back:
|
||||
return GL_BACK;
|
||||
case Maxwell::Cull::CullFace::FrontAndBack:
|
||||
case Maxwell::CullFace::FrontAndBack:
|
||||
return GL_FRONT_AND_BACK;
|
||||
}
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "core/telemetry_session.h"
|
||||
#include "video_core/morton.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
|
||||
namespace OpenGL {
|
||||
@ -86,28 +87,22 @@ public:
|
||||
}
|
||||
|
||||
void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
OpenGLState state = OpenGLState::GetCurState();
|
||||
|
||||
// Recreate the color texture attachment
|
||||
frame->color.Release();
|
||||
frame->color.Create();
|
||||
state.renderbuffer = frame->color.handle;
|
||||
state.Apply();
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, frame->is_srgb ? GL_SRGB8 : GL_RGB8, width, height);
|
||||
const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
|
||||
glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
|
||||
|
||||
// Recreate the FBO for the render target
|
||||
frame->render.Release();
|
||||
frame->render.Create();
|
||||
state.draw.read_framebuffer = frame->render.handle;
|
||||
state.draw.draw_framebuffer = frame->render.handle;
|
||||
state.Apply();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||
frame->color.handle);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
|
||||
}
|
||||
prev_state.Apply();
|
||||
|
||||
frame->width = width;
|
||||
frame->height = height;
|
||||
frame->color_reloaded = true;
|
||||
@ -164,9 +159,13 @@ public:
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char vertex_shader[] = R"(
|
||||
constexpr char VERTEX_SHADER[] = R"(
|
||||
#version 430 core
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
layout (location = 0) in vec2 vert_position;
|
||||
layout (location = 1) in vec2 vert_tex_coord;
|
||||
layout (location = 0) out vec2 frag_tex_coord;
|
||||
@ -187,7 +186,7 @@ void main() {
|
||||
}
|
||||
)";
|
||||
|
||||
constexpr char fragment_shader[] = R"(
|
||||
constexpr char FRAGMENT_SHADER[] = R"(
|
||||
#version 430 core
|
||||
|
||||
layout (location = 0) in vec2 frag_tex_coord;
|
||||
@ -196,7 +195,7 @@ layout (location = 0) out vec4 color;
|
||||
layout (binding = 0) uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
color = texture(color_texture, frag_tex_coord);
|
||||
color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
|
||||
}
|
||||
)";
|
||||
|
||||
@ -205,8 +204,8 @@ constexpr GLint TexCoordLocation = 1;
|
||||
constexpr GLint ModelViewMatrixLocation = 0;
|
||||
|
||||
struct ScreenRectVertex {
|
||||
constexpr ScreenRectVertex(GLfloat x, GLfloat y, GLfloat u, GLfloat v)
|
||||
: position{{x, y}}, tex_coord{{u, v}} {}
|
||||
constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v)
|
||||
: position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
|
||||
|
||||
std::array<GLfloat, 2> position;
|
||||
std::array<GLfloat, 2> tex_coord;
|
||||
@ -311,11 +310,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Maintain the rasterizer's state as a priority
|
||||
OpenGLState prev_state = OpenGLState::GetCurState();
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
|
||||
PrepareRendertarget(framebuffer);
|
||||
RenderScreenshot();
|
||||
|
||||
@ -358,8 +352,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
frame->is_srgb = screen_info.display_srgb;
|
||||
frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
|
||||
}
|
||||
state.draw.draw_framebuffer = frame->render.handle;
|
||||
state.Apply();
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame->render.handle);
|
||||
DrawScreen(layout);
|
||||
// Create a fence for the frontend to wait on and swap this frame to OffTex
|
||||
frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
@ -368,10 +361,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
m_current_frame++;
|
||||
rasterizer->TickFrame();
|
||||
}
|
||||
|
||||
// Restore the rasterizer state
|
||||
prev_state.AllDirty();
|
||||
prev_state.Apply();
|
||||
}
|
||||
|
||||
void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
|
||||
@ -442,31 +431,25 @@ void RendererOpenGL::InitOpenGLObjects() {
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
|
||||
// Link shaders and get variable locations
|
||||
shader.CreateFromSource(vertex_shader, nullptr, fragment_shader);
|
||||
state.draw.shader_program = shader.handle;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
// Create shader programs
|
||||
OGLShader vertex_shader;
|
||||
vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER);
|
||||
|
||||
OGLShader fragment_shader;
|
||||
fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER);
|
||||
|
||||
vertex_program.Create(true, false, vertex_shader.handle);
|
||||
fragment_program.Create(true, false, fragment_shader.handle);
|
||||
|
||||
// Create program pipeline
|
||||
program_manager.Create();
|
||||
glBindProgramPipeline(program_manager.GetHandle());
|
||||
|
||||
// Generate VBO handle for drawing
|
||||
vertex_buffer.Create();
|
||||
|
||||
// Generate VAO
|
||||
vertex_array.Create();
|
||||
state.draw.vertex_array = vertex_array.handle;
|
||||
|
||||
// Attach vertex data to VAO
|
||||
glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
|
||||
glVertexArrayAttribFormat(vertex_array.handle, PositionLocation, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ScreenRectVertex, position));
|
||||
glVertexArrayAttribFormat(vertex_array.handle, TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ScreenRectVertex, tex_coord));
|
||||
glVertexArrayAttribBinding(vertex_array.handle, PositionLocation, 0);
|
||||
glVertexArrayAttribBinding(vertex_array.handle, TexCoordLocation, 0);
|
||||
glEnableVertexArrayAttrib(vertex_array.handle, PositionLocation);
|
||||
glEnableVertexArrayAttrib(vertex_array.handle, TexCoordLocation);
|
||||
glVertexArrayVertexBuffer(vertex_array.handle, 0, vertex_buffer.handle, 0,
|
||||
sizeof(ScreenRectVertex));
|
||||
|
||||
// Allocate textures for the screen
|
||||
screen_info.texture.resource.Create(GL_TEXTURE_2D);
|
||||
@ -499,7 +482,8 @@ void RendererOpenGL::CreateRasterizer() {
|
||||
if (rasterizer) {
|
||||
return;
|
||||
}
|
||||
rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info);
|
||||
rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info,
|
||||
program_manager, state_tracker);
|
||||
}
|
||||
|
||||
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
@ -538,8 +522,19 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
|
||||
glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height);
|
||||
}
|
||||
|
||||
void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w,
|
||||
float h) {
|
||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
// Set projection matrix
|
||||
const std::array ortho_matrix =
|
||||
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
|
||||
glProgramUniformMatrix3x2fv(vertex_program.handle, ModelViewMatrixLocation, 1, GL_FALSE,
|
||||
std::data(ortho_matrix));
|
||||
|
||||
const auto& texcoords = screen_info.display_texcoords;
|
||||
auto left = texcoords.left;
|
||||
auto right = texcoords.right;
|
||||
@ -571,46 +566,77 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
|
||||
static_cast<f32>(screen_info.texture.height);
|
||||
}
|
||||
|
||||
const std::array vertices = {
|
||||
ScreenRectVertex(x, y, texcoords.top * scale_u, left * scale_v),
|
||||
ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left * scale_v),
|
||||
ScreenRectVertex(x, y + h, texcoords.top * scale_u, right * scale_v),
|
||||
ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
|
||||
};
|
||||
|
||||
state.textures[0] = screen_info.display_texture;
|
||||
state.framebuffer_srgb.enabled = screen_info.display_srgb;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
// Restore default state
|
||||
state.framebuffer_srgb.enabled = false;
|
||||
state.textures[0] = 0;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
}
|
||||
|
||||
void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
if (renderer_settings.set_background_color) {
|
||||
// Update background color before drawing
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
const auto& screen = layout.screen;
|
||||
const std::array vertices = {
|
||||
ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u, left * scale_v),
|
||||
ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u, left * scale_v),
|
||||
ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u, right * scale_v),
|
||||
ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u, right * scale_v),
|
||||
};
|
||||
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
|
||||
|
||||
// TODO: Signal state tracker about these changes
|
||||
state_tracker.NotifyScreenDrawVertexArray();
|
||||
state_tracker.NotifyViewport0();
|
||||
state_tracker.NotifyScissor0();
|
||||
state_tracker.NotifyColorMask0();
|
||||
state_tracker.NotifyBlend0();
|
||||
state_tracker.NotifyFramebuffer();
|
||||
state_tracker.NotifyFrontFace();
|
||||
state_tracker.NotifyCullTest();
|
||||
state_tracker.NotifyDepthTest();
|
||||
state_tracker.NotifyStencilTest();
|
||||
state_tracker.NotifyPolygonOffset();
|
||||
state_tracker.NotifyRasterizeEnable();
|
||||
state_tracker.NotifyFramebufferSRGB();
|
||||
state_tracker.NotifyLogicOp();
|
||||
state_tracker.NotifyClipControl();
|
||||
state_tracker.NotifyAlphaTest();
|
||||
|
||||
program_manager.UseVertexShader(vertex_program.handle);
|
||||
program_manager.UseGeometryShader(0);
|
||||
program_manager.UseFragmentShader(fragment_program.handle);
|
||||
program_manager.Update();
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
if (screen_info.display_srgb) {
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
} else {
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
glDisable(GL_RASTERIZER_DISCARD);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDisablei(GL_BLEND, 0);
|
||||
glDisablei(GL_SCISSOR_TEST, 0);
|
||||
glCullFace(GL_BACK);
|
||||
glFrontFace(GL_CW);
|
||||
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
|
||||
glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
|
||||
static_cast<GLfloat>(layout.height));
|
||||
glDepthRangeIndexed(0, 0.0, 0.0);
|
||||
|
||||
glEnableVertexAttribArray(PositionLocation);
|
||||
glEnableVertexAttribArray(TexCoordLocation);
|
||||
glVertexAttribDivisor(PositionLocation, 0);
|
||||
glVertexAttribDivisor(TexCoordLocation, 0);
|
||||
glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ScreenRectVertex, position));
|
||||
glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
|
||||
offsetof(ScreenRectVertex, tex_coord));
|
||||
glVertexAttribBinding(PositionLocation, 0);
|
||||
glVertexAttribBinding(TexCoordLocation, 0);
|
||||
glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
|
||||
|
||||
glBindTextureUnit(0, screen_info.display_texture);
|
||||
glBindSampler(0, 0);
|
||||
|
||||
glViewport(0, 0, layout.width, layout.height);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Set projection matrix
|
||||
const std::array ortho_matrix =
|
||||
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
|
||||
glUniformMatrix3x2fv(ModelViewMatrixLocation, 1, GL_FALSE, ortho_matrix.data());
|
||||
|
||||
DrawScreenTriangles(screen_info, static_cast<float>(screen.left),
|
||||
static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()),
|
||||
static_cast<float>(screen.GetHeight()));
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void RendererOpenGL::TryPresent(int timeout_ms) {
|
||||
@ -653,13 +679,14 @@ void RendererOpenGL::RenderScreenshot() {
|
||||
return;
|
||||
}
|
||||
|
||||
GLint old_read_fb;
|
||||
GLint old_draw_fb;
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
|
||||
|
||||
// Draw the current frame to the screenshot framebuffer
|
||||
screenshot_framebuffer.Create();
|
||||
GLuint old_read_fb = state.draw.read_framebuffer;
|
||||
GLuint old_draw_fb = state.draw.draw_framebuffer;
|
||||
state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
|
||||
|
||||
Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
|
||||
|
||||
@ -676,12 +703,11 @@ void RendererOpenGL::RenderScreenshot() {
|
||||
renderer_settings.screenshot_bits);
|
||||
|
||||
screenshot_framebuffer.Release();
|
||||
state.draw.read_framebuffer = old_read_fb;
|
||||
state.draw.draw_framebuffer = old_draw_fb;
|
||||
state.AllDirty();
|
||||
state.Apply();
|
||||
glDeleteRenderbuffers(1, &renderbuffer);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
|
||||
|
||||
renderer_settings.screenshot_complete_callback();
|
||||
renderer_settings.screenshot_requested = false;
|
||||
}
|
||||
|
@ -10,7 +10,8 @@
|
||||
#include "common/math_util.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@ -76,8 +77,6 @@ private:
|
||||
/// Draws the emulated screens to the emulator window.
|
||||
void DrawScreen(const Layout::FramebufferLayout& layout);
|
||||
|
||||
void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
|
||||
|
||||
void RenderScreenshot();
|
||||
|
||||
/// Loads framebuffer from emulated memory into the active OpenGL texture.
|
||||
@ -93,17 +92,20 @@ private:
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
Core::System& system;
|
||||
|
||||
OpenGLState state;
|
||||
StateTracker state_tracker{system};
|
||||
|
||||
// OpenGL object IDs
|
||||
OGLVertexArray vertex_array;
|
||||
OGLBuffer vertex_buffer;
|
||||
OGLProgram shader;
|
||||
OGLProgram vertex_program;
|
||||
OGLProgram fragment_program;
|
||||
OGLFramebuffer screenshot_framebuffer;
|
||||
|
||||
/// Display information for Switch screen
|
||||
ScreenInfo screen_info;
|
||||
|
||||
/// Global dummy shader pipeline
|
||||
GLShader::ProgramManager program_manager;
|
||||
|
||||
/// OpenGL framebuffer data
|
||||
std::vector<u8> gl_framebuffer_data;
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_opengl/gl_state_tracker.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
|
||||
namespace OpenGL {
|
||||
@ -20,12 +21,12 @@ struct VertexArrayPushBuffer::Entry {
|
||||
GLsizei stride{};
|
||||
};
|
||||
|
||||
VertexArrayPushBuffer::VertexArrayPushBuffer() = default;
|
||||
VertexArrayPushBuffer::VertexArrayPushBuffer(StateTracker& state_tracker)
|
||||
: state_tracker{state_tracker} {}
|
||||
|
||||
VertexArrayPushBuffer::~VertexArrayPushBuffer() = default;
|
||||
|
||||
void VertexArrayPushBuffer::Setup(GLuint vao_) {
|
||||
vao = vao_;
|
||||
void VertexArrayPushBuffer::Setup() {
|
||||
index_buffer = nullptr;
|
||||
vertex_buffers.clear();
|
||||
}
|
||||
@ -41,13 +42,11 @@ void VertexArrayPushBuffer::SetVertexBuffer(GLuint binding_index, const GLuint*
|
||||
|
||||
void VertexArrayPushBuffer::Bind() {
|
||||
if (index_buffer) {
|
||||
glVertexArrayElementBuffer(vao, *index_buffer);
|
||||
state_tracker.BindIndexBuffer(*index_buffer);
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): Find a way to ARB_multi_bind this
|
||||
for (const auto& entry : vertex_buffers) {
|
||||
glVertexArrayVertexBuffer(vao, entry.binding_index, *entry.buffer, entry.offset,
|
||||
entry.stride);
|
||||
glBindVertexBuffer(entry.binding_index, *entry.buffer, entry.offset, entry.stride);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,14 @@
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
class StateTracker;
|
||||
|
||||
class VertexArrayPushBuffer final {
|
||||
public:
|
||||
explicit VertexArrayPushBuffer();
|
||||
explicit VertexArrayPushBuffer(StateTracker& state_tracker);
|
||||
~VertexArrayPushBuffer();
|
||||
|
||||
void Setup(GLuint vao_);
|
||||
void Setup();
|
||||
|
||||
void SetIndexBuffer(const GLuint* buffer);
|
||||
|
||||
@ -28,7 +30,8 @@ public:
|
||||
private:
|
||||
struct Entry;
|
||||
|
||||
GLuint vao{};
|
||||
StateTracker& state_tracker;
|
||||
|
||||
const GLuint* index_buffer{};
|
||||
std::vector<Entry> vertex_buffers;
|
||||
};
|
||||
|
@ -112,19 +112,18 @@ constexpr FixedPipelineState::Rasterizer GetRasterizerState(const Maxwell& regs)
|
||||
const auto& clip = regs.view_volume_clip_control;
|
||||
const bool depth_clamp_enabled = clip.depth_clamp_near == 1 || clip.depth_clamp_far == 1;
|
||||
|
||||
Maxwell::Cull::FrontFace front_face = regs.cull.front_face;
|
||||
Maxwell::FrontFace front_face = regs.front_face;
|
||||
if (regs.screen_y_control.triangle_rast_flip != 0 &&
|
||||
regs.viewport_transform[0].scale_y > 0.0f) {
|
||||
if (front_face == Maxwell::Cull::FrontFace::CounterClockWise)
|
||||
front_face = Maxwell::Cull::FrontFace::ClockWise;
|
||||
else if (front_face == Maxwell::Cull::FrontFace::ClockWise)
|
||||
front_face = Maxwell::Cull::FrontFace::CounterClockWise;
|
||||
if (front_face == Maxwell::FrontFace::CounterClockWise)
|
||||
front_face = Maxwell::FrontFace::ClockWise;
|
||||
else if (front_face == Maxwell::FrontFace::ClockWise)
|
||||
front_face = Maxwell::FrontFace::CounterClockWise;
|
||||
}
|
||||
|
||||
const bool gl_ndc = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne;
|
||||
return FixedPipelineState::Rasterizer(regs.cull.enabled, depth_bias_enabled,
|
||||
depth_clamp_enabled, gl_ndc, regs.cull.cull_face,
|
||||
front_face);
|
||||
return FixedPipelineState::Rasterizer(regs.cull_test_enabled, depth_bias_enabled,
|
||||
depth_clamp_enabled, gl_ndc, regs.cull_face, front_face);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
@ -171,8 +171,8 @@ struct FixedPipelineState {
|
||||
|
||||
struct Rasterizer {
|
||||
constexpr Rasterizer(bool cull_enable, bool depth_bias_enable, bool depth_clamp_enable,
|
||||
bool ndc_minus_one_to_one, Maxwell::Cull::CullFace cull_face,
|
||||
Maxwell::Cull::FrontFace front_face)
|
||||
bool ndc_minus_one_to_one, Maxwell::CullFace cull_face,
|
||||
Maxwell::FrontFace front_face)
|
||||
: cull_enable{cull_enable}, depth_bias_enable{depth_bias_enable},
|
||||
depth_clamp_enable{depth_clamp_enable}, ndc_minus_one_to_one{ndc_minus_one_to_one},
|
||||
cull_face{cull_face}, front_face{front_face} {}
|
||||
@ -182,8 +182,8 @@ struct FixedPipelineState {
|
||||
bool depth_bias_enable;
|
||||
bool depth_clamp_enable;
|
||||
bool ndc_minus_one_to_one;
|
||||
Maxwell::Cull::CullFace cull_face;
|
||||
Maxwell::Cull::FrontFace front_face;
|
||||
Maxwell::CullFace cull_face;
|
||||
Maxwell::FrontFace front_face;
|
||||
|
||||
std::size_t Hash() const noexcept;
|
||||
|
||||
|
@ -586,24 +586,24 @@ vk::BlendFactor BlendFactor(Maxwell::Blend::Factor factor) {
|
||||
return {};
|
||||
}
|
||||
|
||||
vk::FrontFace FrontFace(Maxwell::Cull::FrontFace front_face) {
|
||||
vk::FrontFace FrontFace(Maxwell::FrontFace front_face) {
|
||||
switch (front_face) {
|
||||
case Maxwell::Cull::FrontFace::ClockWise:
|
||||
case Maxwell::FrontFace::ClockWise:
|
||||
return vk::FrontFace::eClockwise;
|
||||
case Maxwell::Cull::FrontFace::CounterClockWise:
|
||||
case Maxwell::FrontFace::CounterClockWise:
|
||||
return vk::FrontFace::eCounterClockwise;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented front face={}", static_cast<u32>(front_face));
|
||||
return {};
|
||||
}
|
||||
|
||||
vk::CullModeFlags CullFace(Maxwell::Cull::CullFace cull_face) {
|
||||
vk::CullModeFlags CullFace(Maxwell::CullFace cull_face) {
|
||||
switch (cull_face) {
|
||||
case Maxwell::Cull::CullFace::Front:
|
||||
case Maxwell::CullFace::Front:
|
||||
return vk::CullModeFlagBits::eFront;
|
||||
case Maxwell::Cull::CullFace::Back:
|
||||
case Maxwell::CullFace::Back:
|
||||
return vk::CullModeFlagBits::eBack;
|
||||
case Maxwell::Cull::CullFace::FrontAndBack:
|
||||
case Maxwell::CullFace::FrontAndBack:
|
||||
return vk::CullModeFlagBits::eFrontAndBack;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented cull face={}", static_cast<u32>(cull_face));
|
||||
|
@ -54,9 +54,9 @@ vk::BlendOp BlendEquation(Maxwell::Blend::Equation equation);
|
||||
|
||||
vk::BlendFactor BlendFactor(Maxwell::Blend::Factor factor);
|
||||
|
||||
vk::FrontFace FrontFace(Maxwell::Cull::FrontFace front_face);
|
||||
vk::FrontFace FrontFace(Maxwell::FrontFace front_face);
|
||||
|
||||
vk::CullModeFlags CullFace(Maxwell::Cull::CullFace cull_face);
|
||||
vk::CullModeFlags CullFace(Maxwell::CullFace cull_face);
|
||||
|
||||
vk::ComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle);
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
|
||||
namespace Vulkan {
|
||||
@ -177,10 +178,13 @@ bool RendererVulkan::Init() {
|
||||
swapchain = std::make_unique<VKSwapchain>(surface, *device);
|
||||
swapchain->Create(framebuffer.width, framebuffer.height, false);
|
||||
|
||||
scheduler = std::make_unique<VKScheduler>(*device, *resource_manager);
|
||||
state_tracker = std::make_unique<StateTracker>(system);
|
||||
|
||||
scheduler = std::make_unique<VKScheduler>(*device, *resource_manager, *state_tracker);
|
||||
|
||||
rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device,
|
||||
*resource_manager, *memory_manager, *scheduler);
|
||||
*resource_manager, *memory_manager,
|
||||
*state_tracker, *scheduler);
|
||||
|
||||
blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device,
|
||||
*resource_manager, *memory_manager, *swapchain,
|
||||
|
@ -4,8 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
|
||||
@ -15,6 +17,7 @@ class System;
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class StateTracker;
|
||||
class VKBlitScreen;
|
||||
class VKDevice;
|
||||
class VKFence;
|
||||
@ -61,6 +64,7 @@ private:
|
||||
std::unique_ptr<VKSwapchain> swapchain;
|
||||
std::unique_ptr<VKMemoryManager> memory_manager;
|
||||
std::unique_ptr<VKResourceManager> resource_manager;
|
||||
std::unique_ptr<StateTracker> state_tracker;
|
||||
std::unique_ptr<VKScheduler> scheduler;
|
||||
std::unique_ptr<VKBlitScreen> blit_screen;
|
||||
};
|
||||
|
@ -188,11 +188,6 @@ VKPipelineCache::~VKPipelineCache() = default;
|
||||
|
||||
std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
|
||||
const auto& gpu = system.GPU().Maxwell3D();
|
||||
auto& dirty = system.GPU().Maxwell3D().dirty.shaders;
|
||||
if (!dirty) {
|
||||
return last_shaders;
|
||||
}
|
||||
dirty = false;
|
||||
|
||||
std::array<Shader, Maxwell::MaxShaderProgram> shaders;
|
||||
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_sampler_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
|
||||
@ -280,10 +281,11 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf,
|
||||
RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& renderer,
|
||||
VKScreenInfo& screen_info, const VKDevice& device,
|
||||
VKResourceManager& resource_manager,
|
||||
VKMemoryManager& memory_manager, VKScheduler& scheduler)
|
||||
VKMemoryManager& memory_manager, StateTracker& state_tracker,
|
||||
VKScheduler& scheduler)
|
||||
: RasterizerAccelerated{system.Memory()}, system{system}, render_window{renderer},
|
||||
screen_info{screen_info}, device{device}, resource_manager{resource_manager},
|
||||
memory_manager{memory_manager}, scheduler{scheduler},
|
||||
memory_manager{memory_manager}, state_tracker{state_tracker}, scheduler{scheduler},
|
||||
staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
|
||||
update_descriptor_queue(device, scheduler),
|
||||
quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
|
||||
@ -548,6 +550,10 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
|
||||
return true;
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SetupDirtyFlags() {
|
||||
state_tracker.Initialize();
|
||||
}
|
||||
|
||||
void RasterizerVulkan::FlushWork() {
|
||||
static constexpr u32 DRAWS_TO_DISPATCH = 4096;
|
||||
|
||||
@ -571,9 +577,9 @@ void RasterizerVulkan::FlushWork() {
|
||||
|
||||
RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
|
||||
MICROPROFILE_SCOPE(Vulkan_RenderTargets);
|
||||
auto& dirty = system.GPU().Maxwell3D().dirty;
|
||||
const bool update_rendertargets = dirty.render_settings;
|
||||
dirty.render_settings = false;
|
||||
auto& dirty = system.GPU().Maxwell3D().dirty.flags;
|
||||
const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets];
|
||||
dirty[VideoCommon::Dirty::RenderTargets] = false;
|
||||
|
||||
texture_cache.GuardRenderTargets(true);
|
||||
|
||||
@ -723,13 +729,13 @@ void RasterizerVulkan::SetupImageTransitions(
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDynamicStates() {
|
||||
auto& gpu = system.GPU().Maxwell3D();
|
||||
UpdateViewportsState(gpu);
|
||||
UpdateScissorsState(gpu);
|
||||
UpdateDepthBias(gpu);
|
||||
UpdateBlendConstants(gpu);
|
||||
UpdateDepthBounds(gpu);
|
||||
UpdateStencilFaces(gpu);
|
||||
auto& regs = system.GPU().Maxwell3D().regs;
|
||||
UpdateViewportsState(regs);
|
||||
UpdateScissorsState(regs);
|
||||
UpdateDepthBias(regs);
|
||||
UpdateBlendConstants(regs);
|
||||
UpdateDepthBounds(regs);
|
||||
UpdateStencilFaces(regs);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
|
||||
@ -979,12 +985,10 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima
|
||||
image_views.push_back(ImageView{std::move(view), image_layout});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) {
|
||||
if (!gpu.dirty.viewport_transform && scheduler.TouchViewports()) {
|
||||
void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchViewports()) {
|
||||
return;
|
||||
}
|
||||
gpu.dirty.viewport_transform = false;
|
||||
const auto& regs = gpu.regs;
|
||||
const std::array viewports{
|
||||
GetViewportState(device, regs, 0), GetViewportState(device, regs, 1),
|
||||
GetViewportState(device, regs, 2), GetViewportState(device, regs, 3),
|
||||
@ -999,12 +1003,10 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) {
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) {
|
||||
if (!gpu.dirty.scissor_test && scheduler.TouchScissors()) {
|
||||
void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchScissors()) {
|
||||
return;
|
||||
}
|
||||
gpu.dirty.scissor_test = false;
|
||||
const auto& regs = gpu.regs;
|
||||
const std::array scissors = {
|
||||
GetScissorState(regs, 0), GetScissorState(regs, 1), GetScissorState(regs, 2),
|
||||
GetScissorState(regs, 3), GetScissorState(regs, 4), GetScissorState(regs, 5),
|
||||
@ -1017,46 +1019,39 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) {
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu) {
|
||||
if (!gpu.dirty.polygon_offset && scheduler.TouchDepthBias()) {
|
||||
void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthBias()) {
|
||||
return;
|
||||
}
|
||||
gpu.dirty.polygon_offset = false;
|
||||
const auto& regs = gpu.regs;
|
||||
scheduler.Record([constant = regs.polygon_offset_units, clamp = regs.polygon_offset_clamp,
|
||||
factor = regs.polygon_offset_factor](auto cmdbuf, auto& dld) {
|
||||
cmdbuf.setDepthBias(constant, clamp, factor / 2.0f, dld);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu) {
|
||||
if (!gpu.dirty.blend_state && scheduler.TouchBlendConstants()) {
|
||||
void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchBlendConstants()) {
|
||||
return;
|
||||
}
|
||||
gpu.dirty.blend_state = false;
|
||||
const std::array blend_color = {gpu.regs.blend_color.r, gpu.regs.blend_color.g,
|
||||
gpu.regs.blend_color.b, gpu.regs.blend_color.a};
|
||||
const std::array blend_color = {regs.blend_color.r, regs.blend_color.g, regs.blend_color.b,
|
||||
regs.blend_color.a};
|
||||
scheduler.Record([blend_color](auto cmdbuf, auto& dld) {
|
||||
cmdbuf.setBlendConstants(blend_color.data(), dld);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D& gpu) {
|
||||
if (!gpu.dirty.depth_bounds_values && scheduler.TouchDepthBounds()) {
|
||||
void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthBounds()) {
|
||||
return;
|
||||
}
|
||||
gpu.dirty.depth_bounds_values = false;
|
||||
const auto& regs = gpu.regs;
|
||||
scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](
|
||||
auto cmdbuf, auto& dld) { cmdbuf.setDepthBounds(min, max, dld); });
|
||||
}
|
||||
|
||||
void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D& gpu) {
|
||||
if (!gpu.dirty.stencil_test && scheduler.TouchStencilValues()) {
|
||||
void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchStencilProperties()) {
|
||||
return;
|
||||
}
|
||||
gpu.dirty.stencil_test = false;
|
||||
const auto& regs = gpu.regs;
|
||||
if (regs.stencil_two_side_enable) {
|
||||
// Separate values per face
|
||||
scheduler.Record(
|
||||
|
@ -96,6 +96,7 @@ struct hash<Vulkan::FramebufferCacheKey> {
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class StateTracker;
|
||||
class BufferBindings;
|
||||
|
||||
struct ImageView {
|
||||
@ -108,7 +109,7 @@ public:
|
||||
explicit RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& render_window,
|
||||
VKScreenInfo& screen_info, const VKDevice& device,
|
||||
VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
|
||||
VKScheduler& scheduler);
|
||||
StateTracker& state_tracker, VKScheduler& scheduler);
|
||||
~RasterizerVulkan() override;
|
||||
|
||||
void Draw(bool is_indexed, bool is_instanced) override;
|
||||
@ -127,6 +128,7 @@ public:
|
||||
const Tegra::Engines::Fermi2D::Config& copy_config) override;
|
||||
bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
|
||||
u32 pixel_stride) override;
|
||||
void SetupDirtyFlags() override;
|
||||
|
||||
/// Maximum supported size that a constbuffer can have in bytes.
|
||||
static constexpr std::size_t MaxConstbufferSize = 0x10000;
|
||||
@ -215,12 +217,12 @@ private:
|
||||
|
||||
void SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry);
|
||||
|
||||
void UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu);
|
||||
void UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu);
|
||||
void UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu);
|
||||
void UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu);
|
||||
void UpdateDepthBounds(Tegra::Engines::Maxwell3D& gpu);
|
||||
void UpdateStencilFaces(Tegra::Engines::Maxwell3D& gpu);
|
||||
void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
|
||||
|
||||
std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
|
||||
|
||||
@ -241,6 +243,7 @@ private:
|
||||
const VKDevice& device;
|
||||
VKResourceManager& resource_manager;
|
||||
VKMemoryManager& memory_manager;
|
||||
StateTracker& state_tracker;
|
||||
VKScheduler& scheduler;
|
||||
|
||||
VKStagingBufferPool staging_pool;
|
||||
|
@ -2,6 +2,12 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
@ -9,6 +15,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_query_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
@ -29,9 +36,10 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
|
||||
last = nullptr;
|
||||
}
|
||||
|
||||
VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager)
|
||||
: device{device}, resource_manager{resource_manager}, next_fence{
|
||||
&resource_manager.CommitFence()} {
|
||||
VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
|
||||
StateTracker& state_tracker)
|
||||
: device{device}, resource_manager{resource_manager}, state_tracker{state_tracker},
|
||||
next_fence{&resource_manager.CommitFence()} {
|
||||
AcquireNewChunk();
|
||||
AllocateNewContext();
|
||||
worker_thread = std::thread(&VKScheduler::WorkerThread, this);
|
||||
@ -157,12 +165,7 @@ void VKScheduler::AllocateNewContext() {
|
||||
|
||||
void VKScheduler::InvalidateState() {
|
||||
state.graphics_pipeline = nullptr;
|
||||
state.viewports = false;
|
||||
state.scissors = false;
|
||||
state.depth_bias = false;
|
||||
state.blend_constants = false;
|
||||
state.depth_bounds = false;
|
||||
state.stencil_values = false;
|
||||
state_tracker.InvalidateCommandBufferState();
|
||||
}
|
||||
|
||||
void VKScheduler::EndPendingOperations() {
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class StateTracker;
|
||||
class VKDevice;
|
||||
class VKFence;
|
||||
class VKQueryCache;
|
||||
@ -43,7 +44,8 @@ private:
|
||||
/// OpenGL-like operations on Vulkan command buffers.
|
||||
class VKScheduler {
|
||||
public:
|
||||
explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager);
|
||||
explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
|
||||
StateTracker& state_tracker);
|
||||
~VKScheduler();
|
||||
|
||||
/// Sends the current execution context to the GPU.
|
||||
@ -74,36 +76,6 @@ public:
|
||||
query_cache = &query_cache_;
|
||||
}
|
||||
|
||||
/// Returns true when viewports have been set in the current command buffer.
|
||||
bool TouchViewports() {
|
||||
return std::exchange(state.viewports, true);
|
||||
}
|
||||
|
||||
/// Returns true when scissors have been set in the current command buffer.
|
||||
bool TouchScissors() {
|
||||
return std::exchange(state.scissors, true);
|
||||
}
|
||||
|
||||
/// Returns true when depth bias have been set in the current command buffer.
|
||||
bool TouchDepthBias() {
|
||||
return std::exchange(state.depth_bias, true);
|
||||
}
|
||||
|
||||
/// Returns true when blend constants have been set in the current command buffer.
|
||||
bool TouchBlendConstants() {
|
||||
return std::exchange(state.blend_constants, true);
|
||||
}
|
||||
|
||||
/// Returns true when depth bounds have been set in the current command buffer.
|
||||
bool TouchDepthBounds() {
|
||||
return std::exchange(state.depth_bounds, true);
|
||||
}
|
||||
|
||||
/// Returns true when stencil values have been set in the current command buffer.
|
||||
bool TouchStencilValues() {
|
||||
return std::exchange(state.stencil_values, true);
|
||||
}
|
||||
|
||||
/// Send work to a separate thread.
|
||||
template <typename T>
|
||||
void Record(T&& command) {
|
||||
@ -217,6 +189,8 @@ private:
|
||||
|
||||
const VKDevice& device;
|
||||
VKResourceManager& resource_manager;
|
||||
StateTracker& state_tracker;
|
||||
|
||||
VKQueryCache* query_cache = nullptr;
|
||||
|
||||
vk::CommandBuffer current_cmdbuf;
|
||||
@ -226,12 +200,6 @@ private:
|
||||
struct State {
|
||||
std::optional<vk::RenderPassBeginInfo> renderpass;
|
||||
vk::Pipeline graphics_pipeline;
|
||||
bool viewports = false;
|
||||
bool scissors = false;
|
||||
bool depth_bias = false;
|
||||
bool blend_constants = false;
|
||||
bool depth_bounds = false;
|
||||
bool stencil_values = false;
|
||||
} state;
|
||||
|
||||
std::unique_ptr<CommandChunk> chunk;
|
||||
|
101
src/video_core/renderer_vulkan/vk_state_tracker.cpp
Normal file
101
src/video_core/renderer_vulkan/vk_state_tracker.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
||||
|
||||
#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
|
||||
#define NUM(field_name) (sizeof(Maxwell3D::Regs::field_name) / sizeof(u32))
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace Dirty;
|
||||
using namespace VideoCommon::Dirty;
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
using Regs = Maxwell3D::Regs;
|
||||
using Tables = Maxwell3D::DirtyState::Tables;
|
||||
using Table = Maxwell3D::DirtyState::Table;
|
||||
using Flags = Maxwell3D::DirtyState::Flags;
|
||||
|
||||
Flags MakeInvalidationFlags() {
|
||||
Flags flags{};
|
||||
flags[Viewports] = true;
|
||||
flags[Scissors] = true;
|
||||
flags[DepthBias] = true;
|
||||
flags[BlendConstants] = true;
|
||||
flags[DepthBounds] = true;
|
||||
flags[StencilProperties] = true;
|
||||
return flags;
|
||||
}
|
||||
|
||||
void SetupDirtyViewports(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
|
||||
FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
|
||||
tables[0][OFF(viewport_transform_enabled)] = Viewports;
|
||||
}
|
||||
|
||||
void SetupDirtyScissors(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(scissor_test), NUM(scissor_test), Scissors);
|
||||
}
|
||||
|
||||
void SetupDirtyDepthBias(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(polygon_offset_units)] = DepthBias;
|
||||
table[OFF(polygon_offset_clamp)] = DepthBias;
|
||||
table[OFF(polygon_offset_factor)] = DepthBias;
|
||||
}
|
||||
|
||||
void SetupDirtyBlendConstants(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendConstants);
|
||||
}
|
||||
|
||||
void SetupDirtyDepthBounds(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(depth_bounds), NUM(depth_bounds), DepthBounds);
|
||||
}
|
||||
|
||||
void SetupDirtyStencilProperties(Tables& tables) {
|
||||
auto& table = tables[0];
|
||||
table[OFF(stencil_two_side_enable)] = StencilProperties;
|
||||
table[OFF(stencil_front_func_ref)] = StencilProperties;
|
||||
table[OFF(stencil_front_mask)] = StencilProperties;
|
||||
table[OFF(stencil_front_func_mask)] = StencilProperties;
|
||||
table[OFF(stencil_back_func_ref)] = StencilProperties;
|
||||
table[OFF(stencil_back_mask)] = StencilProperties;
|
||||
table[OFF(stencil_back_func_mask)] = StencilProperties;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
StateTracker::StateTracker(Core::System& system)
|
||||
: system{system}, invalidation_flags{MakeInvalidationFlags()} {}
|
||||
|
||||
void StateTracker::Initialize() {
|
||||
auto& dirty = system.GPU().Maxwell3D().dirty;
|
||||
auto& tables = dirty.tables;
|
||||
SetupDirtyRenderTargets(tables);
|
||||
SetupDirtyViewports(tables);
|
||||
SetupDirtyScissors(tables);
|
||||
SetupDirtyDepthBias(tables);
|
||||
SetupDirtyBlendConstants(tables);
|
||||
SetupDirtyDepthBounds(tables);
|
||||
SetupDirtyStencilProperties(tables);
|
||||
|
||||
SetupCommonOnWriteStores(dirty.on_write_stores);
|
||||
}
|
||||
|
||||
void StateTracker::InvalidateCommandBufferState() {
|
||||
system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
79
src/video_core/renderer_vulkan/vk_state_tracker.h
Normal file
79
src/video_core/renderer_vulkan/vk_state_tracker.h
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace Dirty {
|
||||
|
||||
enum : u8 {
|
||||
First = VideoCommon::Dirty::LastCommonEntry,
|
||||
|
||||
Viewports,
|
||||
Scissors,
|
||||
DepthBias,
|
||||
BlendConstants,
|
||||
DepthBounds,
|
||||
StencilProperties,
|
||||
|
||||
Last
|
||||
};
|
||||
static_assert(Last <= std::numeric_limits<u8>::max());
|
||||
|
||||
} // namespace Dirty
|
||||
|
||||
class StateTracker {
|
||||
public:
|
||||
explicit StateTracker(Core::System& system);
|
||||
|
||||
void Initialize();
|
||||
|
||||
void InvalidateCommandBufferState();
|
||||
|
||||
bool TouchViewports() {
|
||||
return Exchange(Dirty::Viewports, false);
|
||||
}
|
||||
|
||||
bool TouchScissors() {
|
||||
return Exchange(Dirty::Scissors, false);
|
||||
}
|
||||
|
||||
bool TouchDepthBias() {
|
||||
return Exchange(Dirty::DepthBias, false);
|
||||
}
|
||||
|
||||
bool TouchBlendConstants() {
|
||||
return Exchange(Dirty::BlendConstants, false);
|
||||
}
|
||||
|
||||
bool TouchDepthBounds() {
|
||||
return Exchange(Dirty::DepthBounds, false);
|
||||
}
|
||||
|
||||
bool TouchStencilProperties() {
|
||||
return Exchange(Dirty::StencilProperties, false);
|
||||
}
|
||||
|
||||
private:
|
||||
bool Exchange(std::size_t id, bool new_value) const noexcept {
|
||||
auto& flags = system.GPU().Maxwell3D().dirty.flags;
|
||||
const bool is_dirty = flags[id];
|
||||
flags[id] = new_value;
|
||||
return is_dirty;
|
||||
}
|
||||
|
||||
Core::System& system;
|
||||
Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
@ -22,6 +22,7 @@
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_memory_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
||||
#include "video_core/surface.h"
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/gpu.h"
|
||||
@ -142,11 +143,10 @@ public:
|
||||
TView GetDepthBufferSurface(bool preserve_contents) {
|
||||
std::lock_guard lock{mutex};
|
||||
auto& maxwell3d = system.GPU().Maxwell3D();
|
||||
|
||||
if (!maxwell3d.dirty.depth_buffer) {
|
||||
if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer]) {
|
||||
return depth_buffer.view;
|
||||
}
|
||||
maxwell3d.dirty.depth_buffer = false;
|
||||
maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer] = false;
|
||||
|
||||
const auto& regs{maxwell3d.regs};
|
||||
const auto gpu_addr{regs.zeta.Address()};
|
||||
@ -175,10 +175,10 @@ public:
|
||||
std::lock_guard lock{mutex};
|
||||
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
|
||||
auto& maxwell3d = system.GPU().Maxwell3D();
|
||||
if (!maxwell3d.dirty.render_target[index]) {
|
||||
if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index]) {
|
||||
return render_targets[index].view;
|
||||
}
|
||||
maxwell3d.dirty.render_target[index] = false;
|
||||
maxwell3d.dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index] = false;
|
||||
|
||||
const auto& regs{maxwell3d.regs};
|
||||
if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
|
||||
@ -320,14 +320,14 @@ protected:
|
||||
virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0;
|
||||
|
||||
void ManageRenderTargetUnregister(TSurface& surface) {
|
||||
auto& maxwell3d = system.GPU().Maxwell3D();
|
||||
auto& dirty = system.GPU().Maxwell3D().dirty;
|
||||
const u32 index = surface->GetRenderTarget();
|
||||
if (index == DEPTH_RT) {
|
||||
maxwell3d.dirty.depth_buffer = true;
|
||||
dirty.flags[VideoCommon::Dirty::ZetaBuffer] = true;
|
||||
} else {
|
||||
maxwell3d.dirty.render_target[index] = true;
|
||||
dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index] = true;
|
||||
}
|
||||
maxwell3d.dirty.render_settings = true;
|
||||
dirty.flags[VideoCommon::Dirty::RenderTargets] = true;
|
||||
}
|
||||
|
||||
void Register(TSurface surface) {
|
||||
|
Loading…
Reference in New Issue
Block a user