mirror of https://git.suyu.dev/suyu/suyu
Merge pull request #3301 from ReinUsesLisp/state-tracker
video_core: Remove gl_state and use a state tracker based on dirty flagsmerge-requests/60/head
commit
22e825a3bc
@ -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
|
File diff suppressed because it is too large
Load Diff
@ -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
|
Loading…
Reference in New Issue