Browse Source
Merge pull request #3301 from ReinUsesLisp/state-tracker
Merge pull request #3301 from ReinUsesLisp/state-tracker
video_core: Remove gl_state and use a state tracker based on dirty flagsnce_cpp
committed by
GitHub
50 changed files with 1578 additions and 1891 deletions
-
16src/common/math_util.h
-
1src/core/core.cpp
-
8src/video_core/CMakeLists.txt
-
46src/video_core/dirty_flags.cpp
-
51src/video_core/dirty_flags.h
-
2src/video_core/dma_pusher.cpp
-
2src/video_core/engines/kepler_compute.cpp
-
2src/video_core/engines/kepler_memory.cpp
-
187src/video_core/engines/maxwell_3d.cpp
-
150src/video_core/engines/maxwell_3d.h
-
2src/video_core/engines/maxwell_dma.cpp
-
3src/video_core/rasterizer_interface.h
-
4src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
-
2src/video_core/renderer_opengl/gl_framebuffer_cache.h
-
869src/video_core/renderer_opengl/gl_rasterizer.cpp
-
45src/video_core/renderer_opengl/gl_rasterizer.h
-
28src/video_core/renderer_opengl/gl_resource_manager.cpp
-
25src/video_core/renderer_opengl/gl_resource_manager.h
-
3src/video_core/renderer_opengl/gl_shader_cache.cpp
-
5src/video_core/renderer_opengl/gl_shader_decompiler.cpp
-
2src/video_core/renderer_opengl/gl_shader_decompiler.h
-
16src/video_core/renderer_opengl/gl_shader_manager.cpp
-
18src/video_core/renderer_opengl/gl_shader_manager.h
-
569src/video_core/renderer_opengl/gl_state.cpp
-
251src/video_core/renderer_opengl/gl_state.h
-
238src/video_core/renderer_opengl/gl_state_tracker.cpp
-
204src/video_core/renderer_opengl/gl_state_tracker.h
-
1src/video_core/renderer_opengl/gl_stream_buffer.cpp
-
47src/video_core/renderer_opengl/gl_texture_cache.cpp
-
10src/video_core/renderer_opengl/gl_texture_cache.h
-
14src/video_core/renderer_opengl/maxwell_to_gl.h
-
206src/video_core/renderer_opengl/renderer_opengl.cpp
-
14src/video_core/renderer_opengl/renderer_opengl.h
-
13src/video_core/renderer_opengl/utils.cpp
-
9src/video_core/renderer_opengl/utils.h
-
15src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
-
8src/video_core/renderer_vulkan/fixed_pipeline_state.h
-
14src/video_core/renderer_vulkan/maxwell_to_vk.cpp
-
4src/video_core/renderer_vulkan/maxwell_to_vk.h
-
8src/video_core/renderer_vulkan/renderer_vulkan.cpp
-
4src/video_core/renderer_vulkan/renderer_vulkan.h
-
5src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
-
69src/video_core/renderer_vulkan/vk_rasterizer.cpp
-
17src/video_core/renderer_vulkan/vk_rasterizer.h
-
21src/video_core/renderer_vulkan/vk_scheduler.cpp
-
42src/video_core/renderer_vulkan/vk_scheduler.h
-
101src/video_core/renderer_vulkan/vk_state_tracker.cpp
-
79src/video_core/renderer_vulkan/vk_state_tracker.h
-
1src/video_core/renderer_vulkan/vk_texture_cache.cpp
-
18src/video_core/texture_cache/texture_cache.h
@ -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
|
|||
@ -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 |
|||
869
src/video_core/renderer_opengl/gl_rasterizer.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -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 |
|||
@ -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
|
|||
@ -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 |
|||
@ -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
|
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue