mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-16 03:38:38 +12:00
354 lines
7.8 KiB
C++
354 lines
7.8 KiB
C++
#pragma once
|
|
|
|
#include "capabilities.hpp"
|
|
|
|
#include "Utilities/geometry.h"
|
|
#include <unordered_map>
|
|
|
|
namespace gl
|
|
{
|
|
struct driver_state
|
|
{
|
|
const u32 DEPTH_BOUNDS_MIN = 0xFFFF0001;
|
|
const u32 DEPTH_BOUNDS_MAX = 0xFFFF0002;
|
|
const u32 DEPTH_RANGE_MIN = 0xFFFF0003;
|
|
const u32 DEPTH_RANGE_MAX = 0xFFFF0004;
|
|
|
|
std::unordered_map<GLenum, u32> properties = {};
|
|
std::unordered_map<GLenum, std::array<u32, 4>> indexed_properties = {};
|
|
|
|
GLuint current_program = GL_NONE;
|
|
std::array<std::unordered_map<GLenum, GLuint>, 48> bound_textures{ {} };
|
|
|
|
bool enable(u32 test, GLenum cap)
|
|
{
|
|
auto found = properties.find(cap);
|
|
if (found != properties.end() && found->second == test)
|
|
return !!test;
|
|
|
|
properties[cap] = test;
|
|
|
|
if (test)
|
|
glEnable(cap);
|
|
else
|
|
glDisable(cap);
|
|
|
|
return !!test;
|
|
}
|
|
|
|
bool enablei(u32 test, GLenum cap, u32 index)
|
|
{
|
|
auto found = indexed_properties.find(cap);
|
|
const bool exists = found != indexed_properties.end();
|
|
|
|
if (!exists)
|
|
{
|
|
indexed_properties[cap] = {};
|
|
indexed_properties[cap][index] = test;
|
|
}
|
|
else
|
|
{
|
|
if (found->second[index] == test)
|
|
return !!test;
|
|
|
|
found->second[index] = test;
|
|
}
|
|
|
|
if (test)
|
|
glEnablei(cap, index);
|
|
else
|
|
glDisablei(cap, index);
|
|
|
|
return !!test;
|
|
}
|
|
|
|
bool enable(GLenum cap)
|
|
{
|
|
return enable(GL_TRUE, cap);
|
|
}
|
|
|
|
bool enablei(GLenum cap, u32 index)
|
|
{
|
|
return enablei(GL_TRUE, cap, index);
|
|
}
|
|
|
|
bool disable(GLenum cap)
|
|
{
|
|
return enable(GL_FALSE, cap);
|
|
}
|
|
|
|
bool disablei(GLenum cap, u32 index)
|
|
{
|
|
return enablei(GL_FALSE, cap, index);
|
|
}
|
|
|
|
inline bool test_property(GLenum property, u32 test) const
|
|
{
|
|
auto found = properties.find(property);
|
|
if (found == properties.end())
|
|
return false;
|
|
|
|
return (found->second == test);
|
|
}
|
|
|
|
inline bool test_propertyi(GLenum property, u32 test, GLint index) const
|
|
{
|
|
auto found = indexed_properties.find(property);
|
|
if (found == indexed_properties.end())
|
|
return false;
|
|
|
|
return found->second[index] == test;
|
|
}
|
|
|
|
void depth_func(GLenum func)
|
|
{
|
|
if (!test_property(GL_DEPTH_FUNC, func))
|
|
{
|
|
glDepthFunc(func);
|
|
properties[GL_DEPTH_FUNC] = func;
|
|
}
|
|
}
|
|
|
|
void depth_mask(GLboolean mask)
|
|
{
|
|
if (!test_property(GL_DEPTH_WRITEMASK, mask))
|
|
{
|
|
glDepthMask(mask);
|
|
properties[GL_DEPTH_WRITEMASK] = mask;
|
|
}
|
|
}
|
|
|
|
void clear_depth(GLfloat depth)
|
|
{
|
|
u32 value = std::bit_cast<u32>(depth);
|
|
if (!test_property(GL_DEPTH_CLEAR_VALUE, value))
|
|
{
|
|
glClearDepth(depth);
|
|
properties[GL_DEPTH_CLEAR_VALUE] = value;
|
|
}
|
|
}
|
|
|
|
void stencil_mask(GLuint mask)
|
|
{
|
|
if (!test_property(GL_STENCIL_WRITEMASK, mask))
|
|
{
|
|
glStencilMask(mask);
|
|
properties[GL_STENCIL_WRITEMASK] = mask;
|
|
}
|
|
}
|
|
|
|
void clear_stencil(GLint stencil)
|
|
{
|
|
u32 value = std::bit_cast<u32>(stencil);
|
|
if (!test_property(GL_STENCIL_CLEAR_VALUE, value))
|
|
{
|
|
glClearStencil(stencil);
|
|
properties[GL_STENCIL_CLEAR_VALUE] = value;
|
|
}
|
|
}
|
|
|
|
void color_maski(GLint index, u32 mask)
|
|
{
|
|
if (!test_propertyi(GL_COLOR_WRITEMASK, mask, index))
|
|
{
|
|
glColorMaski(index, ((mask & 0x10) ? 1 : 0), ((mask & 0x20) ? 1 : 0), ((mask & 0x40) ? 1 : 0), ((mask & 0x80) ? 1 : 0));
|
|
indexed_properties[GL_COLOR_WRITEMASK][index] = mask;
|
|
}
|
|
}
|
|
|
|
void color_maski(GLint index, bool r, bool g, bool b, bool a)
|
|
{
|
|
u32 mask = 0;
|
|
if (r) mask |= 0x10;
|
|
if (g) mask |= 0x20;
|
|
if (b) mask |= 0x40;
|
|
if (a) mask |= 0x80;
|
|
|
|
color_maski(index, mask);
|
|
}
|
|
|
|
void clear_color(u8 r, u8 g, u8 b, u8 a)
|
|
{
|
|
u32 value = u32{ r } | u32{ g } << 8 | u32{ b } << 16 | u32{ a } << 24;
|
|
if (!test_property(GL_COLOR_CLEAR_VALUE, value))
|
|
{
|
|
glClearColor(r / 255.f, g / 255.f, b / 255.f, a / 255.f);
|
|
properties[GL_COLOR_CLEAR_VALUE] = value;
|
|
}
|
|
}
|
|
|
|
void clear_color(const color4f& color)
|
|
{
|
|
clear_color(static_cast<u8>(color.r * 255), static_cast<u8>(color.g * 255), static_cast<u8>(color.b * 255), static_cast<u8>(color.a * 255));
|
|
}
|
|
|
|
void depth_bounds(float min, float max)
|
|
{
|
|
u32 depth_min = std::bit_cast<u32>(min);
|
|
u32 depth_max = std::bit_cast<u32>(max);
|
|
|
|
if (!test_property(DEPTH_BOUNDS_MIN, depth_min) || !test_property(DEPTH_BOUNDS_MAX, depth_max))
|
|
{
|
|
if (get_driver_caps().NV_depth_buffer_float_supported)
|
|
{
|
|
glDepthBoundsdNV(min, max);
|
|
}
|
|
else
|
|
{
|
|
glDepthBoundsEXT(min, max);
|
|
}
|
|
|
|
properties[DEPTH_BOUNDS_MIN] = depth_min;
|
|
properties[DEPTH_BOUNDS_MAX] = depth_max;
|
|
}
|
|
}
|
|
|
|
void depth_range(float min, float max)
|
|
{
|
|
u32 depth_min = std::bit_cast<u32>(min);
|
|
u32 depth_max = std::bit_cast<u32>(max);
|
|
|
|
if (!test_property(DEPTH_RANGE_MIN, depth_min) || !test_property(DEPTH_RANGE_MAX, depth_max))
|
|
{
|
|
if (get_driver_caps().NV_depth_buffer_float_supported)
|
|
{
|
|
glDepthRangedNV(min, max);
|
|
}
|
|
else
|
|
{
|
|
glDepthRange(min, max);
|
|
}
|
|
|
|
properties[DEPTH_RANGE_MIN] = depth_min;
|
|
properties[DEPTH_RANGE_MAX] = depth_max;
|
|
}
|
|
}
|
|
|
|
void logic_op(GLenum op)
|
|
{
|
|
if (!test_property(GL_COLOR_LOGIC_OP, op))
|
|
{
|
|
glLogicOp(op);
|
|
properties[GL_COLOR_LOGIC_OP] = op;
|
|
}
|
|
}
|
|
|
|
void line_width(GLfloat width)
|
|
{
|
|
u32 value = std::bit_cast<u32>(width);
|
|
|
|
if (!test_property(GL_LINE_WIDTH, value))
|
|
{
|
|
glLineWidth(width);
|
|
properties[GL_LINE_WIDTH] = value;
|
|
}
|
|
}
|
|
|
|
void front_face(GLenum face)
|
|
{
|
|
if (!test_property(GL_FRONT_FACE, face))
|
|
{
|
|
glFrontFace(face);
|
|
properties[GL_FRONT_FACE] = face;
|
|
}
|
|
}
|
|
|
|
void cull_face(GLenum mode)
|
|
{
|
|
if (!test_property(GL_CULL_FACE_MODE, mode))
|
|
{
|
|
glCullFace(mode);
|
|
properties[GL_CULL_FACE_MODE] = mode;
|
|
}
|
|
}
|
|
|
|
void polygon_offset(float factor, float units)
|
|
{
|
|
u32 _units = std::bit_cast<u32>(units);
|
|
u32 _factor = std::bit_cast<u32>(factor);
|
|
|
|
if (!test_property(GL_POLYGON_OFFSET_UNITS, _units) || !test_property(GL_POLYGON_OFFSET_FACTOR, _factor))
|
|
{
|
|
glPolygonOffset(factor, units);
|
|
|
|
properties[GL_POLYGON_OFFSET_UNITS] = _units;
|
|
properties[GL_POLYGON_OFFSET_FACTOR] = _factor;
|
|
}
|
|
}
|
|
|
|
void use_program(GLuint program)
|
|
{
|
|
if (current_program == program)
|
|
{
|
|
return;
|
|
}
|
|
|
|
current_program = program;
|
|
glUseProgram(program);
|
|
}
|
|
|
|
void bind_texture(GLuint layer, GLenum target, GLuint name)
|
|
{
|
|
ensure(layer < 48);
|
|
|
|
auto& bound = bound_textures[layer][target];
|
|
if (bound != name)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + layer);
|
|
glBindTexture(target, name);
|
|
|
|
bound = name;
|
|
}
|
|
}
|
|
|
|
void unbind_texture(GLenum target, GLuint name)
|
|
{
|
|
// To be called with glDeleteTextures.
|
|
// OpenGL internally unbinds the texture on delete, but then reuses the same ID when GenTextures is called again!
|
|
// This can also be avoided using unique internal names, such as 64-bit handles, but that involves changing a lot of code for little benefit
|
|
for (auto& layer : bound_textures)
|
|
{
|
|
if (layer.empty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (auto found = layer.find(target);
|
|
found != layer.end() && found->second == name)
|
|
{
|
|
// Actually still bound!
|
|
found->second = GL_NONE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class command_context
|
|
{
|
|
driver_state* drv;
|
|
|
|
public:
|
|
command_context()
|
|
: drv(nullptr)
|
|
{}
|
|
|
|
command_context(driver_state& drv_)
|
|
: drv(&drv_)
|
|
{}
|
|
|
|
driver_state* operator -> () {
|
|
return drv;
|
|
}
|
|
};
|
|
|
|
void set_command_context(gl::command_context& ctx);
|
|
void set_command_context(gl::driver_state& ctx);
|
|
gl::command_context get_command_context();
|
|
|
|
void set_primary_context_thread(bool = true);
|
|
bool is_primary_context_thread();
|
|
|
|
class fence;
|
|
void flush_command_queue(fence& fence_obj);
|
|
}
|