gl: Refactor image and command-context handling

- Move texture object code out of the monolithic header
- All texture binds go through the shared state
- Transient texture binds use a dedicated temp image slot shared with native UI
This commit is contained in:
kd-11 2022-06-03 23:31:05 +03:00 committed by kd-11
parent 0e5514003a
commit d577cebd89
12 changed files with 1256 additions and 1202 deletions

View File

@ -455,6 +455,8 @@ target_sources(rpcs3_emu PRIVATE
RSX/Capture/rsx_capture.cpp
RSX/Capture/rsx_replay.cpp
RSX/GL/glutils/buffer_object.cpp
RSX/GL/glutils/common.cpp
RSX/GL/glutils/image.cpp
RSX/GL/glutils/ring_buffer.cpp
RSX/GL/GLCommonDecompiler.cpp
RSX/GL/GLCompute.cpp

View File

@ -96,6 +96,7 @@ void GLGSRender::on_init_thread()
m_occlusion_type = g_cfg.video.precise_zpass_count ? GL_SAMPLES_PASSED : GL_ANY_SAMPLES_PASSED;
gl::init();
gl::set_command_context(gl_state);
//Enable adaptive vsync if vsync is requested
gl::set_swapinterval(g_cfg.video.vsync ? -1 : 0);

View File

@ -14,18 +14,6 @@ namespace gl
capabilities g_driver_caps;
const fbo screen{};
static thread_local bool s_tls_primary_context_thread = false;
void set_primary_context_thread(bool value)
{
s_tls_primary_context_thread = value;
}
bool is_primary_context_thread()
{
return s_tls_primary_context_thread;
}
void flush_command_queue(fence& fence_obj)
{
fence_obj.check_signaled();

View File

@ -7,7 +7,6 @@
#include <unordered_map>
#include <algorithm>
#include "GLExecutionState.h"
#include "../GCM.h"
#include "../Common/TextureUtils.h"
#include "../Program/GLSLTypes.h"
@ -22,33 +21,15 @@
#include "glutils/common.h"
// TODO: Include on use
#include "glutils/buffer_object.h"
#define GL_FRAGMENT_TEXTURES_START 0
#define GL_VERTEX_TEXTURES_START (GL_FRAGMENT_TEXTURES_START + 16)
#define GL_STENCIL_MIRRORS_START (GL_VERTEX_TEXTURES_START + 4)
#define GL_STREAM_BUFFER_START (GL_STENCIL_MIRRORS_START + 16)
#define UBO_SLOT(x) (x)
#define SSBO_SLOT(x) (x)
#define GL_VERTEX_PARAMS_BIND_SLOT UBO_SLOT(0)
#define GL_VERTEX_LAYOUT_BIND_SLOT UBO_SLOT(1)
#define GL_VERTEX_CONSTANT_BUFFERS_BIND_SLOT UBO_SLOT(2)
#define GL_FRAGMENT_CONSTANT_BUFFERS_BIND_SLOT UBO_SLOT(3)
#define GL_FRAGMENT_STATE_BIND_SLOT UBO_SLOT(4)
#define GL_FRAGMENT_TEXTURE_PARAMS_BIND_SLOT UBO_SLOT(5)
#define GL_RASTERIZER_STATE_BIND_SLOT UBO_SLOT(6)
#define GL_INTERPRETER_VERTEX_BLOCK SSBO_SLOT(0)
#define GL_INTERPRETER_FRAGMENT_BLOCK SSBO_SLOT(1)
#define GL_COMPUTE_BUFFER_SLOT(index) SSBO_SLOT(2 + index)
#include "glutils/image.h"
#include "glutils/pixel_settings.hpp"
#include "glutils/state_tracker.hpp"
// Noop keyword outside of Windows (used in log_debug)
#if !defined(_WIN32) && !defined(APIENTRY)
#define APIENTRY
#endif
//using enum rsx::format_class;
using namespace ::rsx::format_class_;
namespace gl
{
@ -56,13 +37,8 @@ namespace gl
bool is_primitive_native(rsx::primitive_type in);
GLenum draw_mode(rsx::primitive_type in);
void set_primary_context_thread(bool = true);
bool is_primary_context_thread();
void flush_command_queue(fence& fence_obj);
// Texture helpers
std::array<GLenum, 4> apply_swizzle_remap(const std::array<GLenum, 4>& swizzle_remap, const std::pair<std::array<u8, 4>, std::array<u8, 4>>& decoded_remap);
class exception : public std::exception
{
protected:
@ -127,157 +103,6 @@ namespace gl
depth_stencil = depth | stencil
};
class pixel_pack_settings
{
bool m_swap_bytes = false;
bool m_lsb_first = false;
int m_row_length = 0;
int m_image_height = 0;
int m_skip_rows = 0;
int m_skip_pixels = 0;
int m_skip_images = 0;
int m_alignment = 4;
public:
void apply() const
{
glPixelStorei(GL_PACK_SWAP_BYTES, m_swap_bytes ? GL_TRUE : GL_FALSE);
glPixelStorei(GL_PACK_LSB_FIRST, m_lsb_first ? GL_TRUE : GL_FALSE);
glPixelStorei(GL_PACK_ROW_LENGTH, m_row_length);
glPixelStorei(GL_PACK_IMAGE_HEIGHT, m_image_height);
glPixelStorei(GL_PACK_SKIP_ROWS, m_skip_rows);
glPixelStorei(GL_PACK_SKIP_PIXELS, m_skip_pixels);
glPixelStorei(GL_PACK_SKIP_IMAGES, m_skip_images);
glPixelStorei(GL_PACK_ALIGNMENT, m_alignment);
}
pixel_pack_settings& swap_bytes(bool value = true)
{
m_swap_bytes = value;
return *this;
}
pixel_pack_settings& lsb_first(bool value = true)
{
m_lsb_first = value;
return *this;
}
pixel_pack_settings& row_length(int value)
{
m_row_length = value;
return *this;
}
pixel_pack_settings& image_height(int value)
{
m_image_height = value;
return *this;
}
pixel_pack_settings& skip_rows(int value)
{
m_skip_rows = value;
return *this;
}
pixel_pack_settings& skip_pixels(int value)
{
m_skip_pixels = value;
return *this;
}
pixel_pack_settings& skip_images(int value)
{
m_skip_images = value;
return *this;
}
pixel_pack_settings& alignment(int value)
{
m_alignment = value;
return *this;
}
bool get_swap_bytes() const
{
return m_swap_bytes;
}
int get_row_length() const
{
return m_row_length;
}
};
class pixel_unpack_settings
{
bool m_swap_bytes = false;
bool m_lsb_first = false;
int m_row_length = 0;
int m_image_height = 0;
int m_skip_rows = 0;
int m_skip_pixels = 0;
int m_skip_images = 0;
int m_alignment = 4;
public:
void apply() const
{
glPixelStorei(GL_UNPACK_SWAP_BYTES, m_swap_bytes ? GL_TRUE : GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST, m_lsb_first ? GL_TRUE : GL_FALSE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, m_row_length);
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, m_image_height);
glPixelStorei(GL_UNPACK_SKIP_ROWS, m_skip_rows);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, m_skip_pixels);
glPixelStorei(GL_UNPACK_SKIP_IMAGES, m_skip_images);
glPixelStorei(GL_UNPACK_ALIGNMENT, m_alignment);
}
pixel_unpack_settings& swap_bytes(bool value = true)
{
m_swap_bytes = value;
return *this;
}
pixel_unpack_settings& lsb_first(bool value = true)
{
m_lsb_first = value;
return *this;
}
pixel_unpack_settings& row_length(int value)
{
m_row_length = value;
return *this;
}
pixel_unpack_settings& image_height(int value)
{
m_image_height = value;
return *this;
}
pixel_unpack_settings& skip_rows(int value)
{
m_skip_rows = value;
return *this;
}
pixel_unpack_settings& skip_pixels(int value)
{
m_skip_pixels = value;
return *this;
}
pixel_unpack_settings& skip_images(int value)
{
m_skip_images = value;
return *this;
}
pixel_unpack_settings& alignment(int value)
{
m_alignment = value;
return *this;
}
bool get_swap_bytes() const
{
return m_swap_bytes;
}
int get_row_length() const
{
return m_row_length;
}
};
class vao;
class attrib_t;
@ -574,694 +399,6 @@ namespace gl
}
};
enum image_aspect : u32
{
color = 1,
depth = 2,
stencil = 4
};
class texture
{
friend class texture_view;
public:
enum class type
{
ubyte = GL_UNSIGNED_BYTE,
ushort = GL_UNSIGNED_SHORT,
uint = GL_UNSIGNED_INT,
ubyte_3_3_2 = GL_UNSIGNED_BYTE_3_3_2,
ubyte_2_3_3_rev = GL_UNSIGNED_BYTE_2_3_3_REV,
ushort_5_6_5 = GL_UNSIGNED_SHORT_5_6_5,
ushort_5_6_5_rev = GL_UNSIGNED_SHORT_5_6_5_REV,
ushort_4_4_4_4 = GL_UNSIGNED_SHORT_4_4_4_4,
ushort_4_4_4_4_rev = GL_UNSIGNED_SHORT_4_4_4_4_REV,
ushort_5_5_5_1 = GL_UNSIGNED_SHORT_5_5_5_1,
ushort_1_5_5_5_rev = GL_UNSIGNED_SHORT_1_5_5_5_REV,
uint_8_8_8_8 = GL_UNSIGNED_INT_8_8_8_8,
uint_8_8_8_8_rev = GL_UNSIGNED_INT_8_8_8_8_REV,
uint_10_10_10_2 = GL_UNSIGNED_INT_10_10_10_2,
uint_2_10_10_10_rev = GL_UNSIGNED_INT_2_10_10_10_REV,
uint_24_8 = GL_UNSIGNED_INT_24_8,
float32_uint8 = GL_FLOAT_32_UNSIGNED_INT_24_8_REV,
sbyte = GL_BYTE,
sshort = GL_SHORT,
sint = GL_INT,
f16 = GL_HALF_FLOAT,
f32 = GL_FLOAT,
f64 = GL_DOUBLE,
};
enum class channel
{
zero = GL_ZERO,
one = GL_ONE,
r = GL_RED,
g = GL_GREEN,
b = GL_BLUE,
a = GL_ALPHA,
};
enum class format
{
r = GL_RED,
rg = GL_RG,
rgb = GL_RGB,
rgba = GL_RGBA,
bgr = GL_BGR,
bgra = GL_BGRA,
stencil = GL_STENCIL_INDEX,
depth = GL_DEPTH_COMPONENT,
depth_stencil = GL_DEPTH_STENCIL
};
enum class internal_format
{
stencil8 = GL_STENCIL_INDEX8,
depth16 = GL_DEPTH_COMPONENT16,
depth32f = GL_DEPTH_COMPONENT32F,
depth24_stencil8 = GL_DEPTH24_STENCIL8,
depth32f_stencil8 = GL_DEPTH32F_STENCIL8,
compressed_rgb_s3tc_dxt1 = GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
compressed_rgba_s3tc_dxt1 = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
compressed_rgba_s3tc_dxt3 = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
compressed_rgba_s3tc_dxt5 = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
//Sized internal formats, see opengl spec document on glTexImage2D, table 3
rgba8 = GL_RGBA8,
rgb565 = GL_RGB565,
rgb5a1 = GL_RGB5_A1,
rgba4 = GL_RGBA4,
r8 = GL_R8,
r16 = GL_R16,
r32f = GL_R32F,
rg8 = GL_RG8,
rg16 = GL_RG16,
rg16f = GL_RG16F,
rgba16f = GL_RGBA16F,
rgba32f = GL_RGBA32F
};
enum class wrap
{
repeat = GL_REPEAT,
mirrored_repeat = GL_MIRRORED_REPEAT,
clamp_to_edge = GL_CLAMP_TO_EDGE,
clamp_to_border = GL_CLAMP_TO_BORDER,
mirror_clamp = GL_MIRROR_CLAMP_EXT,
//mirror_clamp_to_edge = GL_MIRROR_CLAMP_TO_EDGE,
mirror_clamp_to_border = GL_MIRROR_CLAMP_TO_BORDER_EXT
};
enum class compare_mode
{
none = GL_NONE,
ref_to_texture = GL_COMPARE_REF_TO_TEXTURE
};
enum class target
{
texture1D = GL_TEXTURE_1D,
texture2D = GL_TEXTURE_2D,
texture3D = GL_TEXTURE_3D,
textureCUBE = GL_TEXTURE_CUBE_MAP,
textureBuffer = GL_TEXTURE_BUFFER
};
protected:
GLuint m_id = GL_NONE;
GLuint m_width = 0;
GLuint m_height = 0;
GLuint m_depth = 0;
GLuint m_mipmaps = 0;
GLuint m_pitch = 0;
GLuint m_compressed = GL_FALSE;
GLuint m_aspect_flags = 0;
target m_target = target::texture2D;
internal_format m_internal_format = internal_format::rgba8;
std::array<GLenum, 4> m_component_layout;
rsx::format_class m_format_class = RSX_FORMAT_CLASS_UNDEFINED;
class save_binding_state
{
GLenum target = GL_NONE;
GLuint old_binding = GL_NONE;
public:
save_binding_state(GLenum target)
{
this->target = target;
switch (target)
{
case GL_TEXTURE_1D:
glGetIntegerv(GL_TEXTURE_BINDING_1D, reinterpret_cast<GLint*>(&old_binding));
break;
case GL_TEXTURE_2D:
glGetIntegerv(GL_TEXTURE_BINDING_2D, reinterpret_cast<GLint*>(&old_binding));
break;
case GL_TEXTURE_3D:
glGetIntegerv(GL_TEXTURE_BINDING_3D, reinterpret_cast<GLint*>(&old_binding));
break;
case GL_TEXTURE_CUBE_MAP:
glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, reinterpret_cast<GLint*>(&old_binding));
break;
case GL_TEXTURE_2D_ARRAY:
glGetIntegerv(GL_TEXTURE_BINDING_2D_ARRAY, reinterpret_cast<GLint*>(&old_binding));
break;
case GL_TEXTURE_BUFFER:
glGetIntegerv(GL_TEXTURE_BINDING_BUFFER, reinterpret_cast<GLint*>(&old_binding));
break;
}
}
~save_binding_state()
{
glBindTexture(target, old_binding);
}
};
public:
texture(const texture&) = delete;
texture(texture&& texture_) = delete;
texture(GLenum target, GLuint width, GLuint height, GLuint depth, GLuint mipmaps, GLenum sized_format,
rsx::format_class format_class = rsx::RSX_FORMAT_CLASS_UNDEFINED)
{
save_binding_state save(target);
glGenTextures(1, &m_id);
glBindTexture(target, m_id); //Must bind to initialize the new texture
switch (target)
{
default:
fmt::throw_exception("Invalid image target 0x%X", target);
case GL_TEXTURE_1D:
glTexStorage1D(target, mipmaps, sized_format, width);
height = depth = 1;
break;
case GL_TEXTURE_2D:
case GL_TEXTURE_CUBE_MAP:
glTexStorage2D(target, mipmaps, sized_format, width, height);
depth = 1;
break;
case GL_TEXTURE_3D:
case GL_TEXTURE_2D_ARRAY:
glTexStorage3D(target, mipmaps, sized_format, width, height, depth);
break;
case GL_TEXTURE_BUFFER:
break;
}
if (target != GL_TEXTURE_BUFFER)
{
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1);
m_width = width;
m_height = height;
m_depth = depth;
m_mipmaps = mipmaps;
m_aspect_flags = image_aspect::color;
switch (sized_format)
{
case GL_DEPTH_COMPONENT16:
{
m_pitch = width * 2;
m_aspect_flags = image_aspect::depth;
break;
}
case GL_DEPTH_COMPONENT32F:
{
m_pitch = width * 4;
m_aspect_flags = image_aspect::depth;
break;
}
case GL_DEPTH24_STENCIL8:
case GL_DEPTH32F_STENCIL8:
{
m_pitch = width * 4;
m_aspect_flags = image_aspect::depth | image_aspect::stencil;
break;
}
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
{
m_compressed = true;
m_pitch = utils::align(width, 4) / 2;
break;
}
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
{
m_compressed = true;
m_pitch = utils::align(width, 4);
break;
}
default:
{
GLenum query_target = (target == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
GLint r, g, b, a;
glGetTexLevelParameteriv(query_target, 0, GL_TEXTURE_RED_SIZE, &r);
glGetTexLevelParameteriv(query_target, 0, GL_TEXTURE_GREEN_SIZE, &g);
glGetTexLevelParameteriv(query_target, 0, GL_TEXTURE_BLUE_SIZE, &b);
glGetTexLevelParameteriv(query_target, 0, GL_TEXTURE_ALPHA_SIZE, &a);
m_pitch = width * (r + g + b + a) / 8;
break;
}
}
if (!m_pitch)
{
fmt::throw_exception("Unhandled GL format 0x%X", sized_format);
}
if (format_class == RSX_FORMAT_CLASS_UNDEFINED)
{
if (m_aspect_flags != image_aspect::color)
{
rsx_log.error("Undefined format class for depth texture is not allowed");
}
else
{
format_class = RSX_FORMAT_CLASS_COLOR;
}
}
}
m_target = static_cast<texture::target>(target);
m_internal_format = static_cast<internal_format>(sized_format);
m_component_layout = { GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE };
m_format_class = format_class;
}
virtual ~texture()
{
if (m_id != GL_NONE)
{
glDeleteTextures(1, &m_id);
m_id = GL_NONE;
}
}
void set_native_component_layout(const std::array<GLenum, 4>& layout)
{
m_component_layout[0] = layout[0];
m_component_layout[1] = layout[1];
m_component_layout[2] = layout[2];
m_component_layout[3] = layout[3];
}
target get_target() const noexcept
{
return m_target;
}
static bool compressed_format(internal_format format_) noexcept
{
switch (format_)
{
case internal_format::compressed_rgb_s3tc_dxt1:
case internal_format::compressed_rgba_s3tc_dxt1:
case internal_format::compressed_rgba_s3tc_dxt3:
case internal_format::compressed_rgba_s3tc_dxt5:
return true;
default:
return false;
}
}
uint id() const noexcept
{
return m_id;
}
explicit operator bool() const noexcept
{
return (m_id != GL_NONE);
}
GLuint width() const
{
return m_width;
}
GLuint height() const
{
return m_height;
}
GLuint depth() const
{
return m_depth;
}
GLuint levels() const
{
return m_mipmaps;
}
GLuint pitch() const
{
return m_pitch;
}
constexpr GLubyte samples() const
{
return 1;
}
GLboolean compressed() const
{
return m_compressed;
}
GLuint aspect() const
{
return m_aspect_flags;
}
rsx::format_class format_class() const
{
return m_format_class;
}
sizeu size2D() const
{
return{ m_width, m_height };
}
size3u size3D() const
{
const auto depth = (m_target == target::textureCUBE) ? 6 : m_depth;
return{ m_width, m_height, depth };
}
texture::internal_format get_internal_format() const
{
return m_internal_format;
}
std::array<GLenum, 4> get_native_component_layout() const
{
return m_component_layout;
}
void copy_from(const void* src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings)
{
pixel_settings.apply();
switch (const auto target_ =static_cast<GLenum>(m_target))
{
case GL_TEXTURE_1D:
{
DSA_CALL(TextureSubImage1D, m_id, GL_TEXTURE_1D, level, region.x, region.width, static_cast<GLenum>(format), static_cast<GLenum>(type), src);
break;
}
case GL_TEXTURE_2D:
{
DSA_CALL(TextureSubImage2D, m_id, GL_TEXTURE_2D, level, region.x, region.y, region.width, region.height, static_cast<GLenum>(format), static_cast<GLenum>(type), src);
break;
}
case GL_TEXTURE_3D:
case GL_TEXTURE_2D_ARRAY:
{
DSA_CALL(TextureSubImage3D, m_id, target_, level, region.x, region.y, region.z, region.width, region.height, region.depth, static_cast<GLenum>(format), static_cast<GLenum>(type), src);
break;
}
case GL_TEXTURE_CUBE_MAP:
{
if (get_driver_caps().ARB_dsa_supported)
{
glTextureSubImage3D(m_id, level, region.x, region.y, region.z, region.width, region.height, region.depth, static_cast<GLenum>(format), static_cast<GLenum>(type), src);
}
else
{
rsx_log.warning("Cubemap upload via texture::copy_from is halfplemented!");
auto ptr = static_cast<const u8*>(src);
const auto end = std::min(6u, region.z + region.depth);
for (unsigned face = region.z; face < end; ++face)
{
glTextureSubImage2DEXT(m_id, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, region.x, region.y, region.width, region.height, static_cast<GLenum>(format), static_cast<GLenum>(type), ptr);
ptr += (region.width * region.height * 4); //TODO
}
}
break;
}
}
}
void copy_from(const void* src, texture::format format, texture::type type, const pixel_unpack_settings& pixel_settings)
{
const coord3u region = { {}, size3D() };
copy_from(src, format, type, 0, region, pixel_settings);
}
void copy_from(buffer &buf, u32 gl_format_type, u32 offset, u32 length)
{
if (get_target() != target::textureBuffer)
fmt::throw_exception("OpenGL error: texture cannot copy from buffer");
DSA_CALL(TextureBufferRange, m_id, GL_TEXTURE_BUFFER, gl_format_type, buf.id(), offset, length);
}
void copy_from(buffer_view &view)
{
copy_from(*view.value(), view.format(), view.offset(), view.range());
}
void copy_to(void* dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const
{
pixel_settings.apply();
const auto& caps = get_driver_caps();
if (!region.x && !region.y && !region.z &&
region.width == m_width && region.height == m_height && region.depth == m_depth)
{
if (caps.ARB_dsa_supported)
glGetTextureImage(m_id, level, static_cast<GLenum>(format), static_cast<GLenum>(type), s32{smax}, dst);
else
glGetTextureImageEXT(m_id, static_cast<GLenum>(m_target), level, static_cast<GLenum>(format), static_cast<GLenum>(type), dst);
}
else if (caps.ARB_dsa_supported)
{
glGetTextureSubImage(m_id, level, region.x, region.y, region.z, region.width, region.height, region.depth,
static_cast<GLenum>(format), static_cast<GLenum>(type), s32{smax}, dst);
}
else
{
// Worst case scenario. For some reason, EXT_dsa does not have glGetTextureSubImage
const auto target_ = static_cast<GLenum>(m_target);
texture tmp{ target_, region.width, region.height, region.depth, 1, static_cast<GLenum>(m_internal_format) };
glCopyImageSubData(m_id, target_, level, region.x, region.y, region.z, tmp.id(), target_, 0, 0, 0, 0,
region.width, region.height, region.depth);
const coord3u region2 = { {0, 0, 0}, region.size };
tmp.copy_to(dst, format, type, 0, region2, pixel_settings);
}
}
void copy_to(void* dst, texture::format format, texture::type type, const pixel_pack_settings& pixel_settings) const
{
const coord3u region = { {}, size3D() };
copy_to(dst, format, type, 0, region, pixel_settings);
}
};
class texture_view
{
GLuint m_id = GL_NONE;
GLenum m_target = 0;
GLenum m_format = 0;
GLenum m_aspect_flags = 0;
texture *m_image_data = nullptr;
GLenum component_swizzle[4];
void create(texture* data, GLenum target, GLenum sized_format, GLenum aspect_flags, const GLenum* argb_swizzle = nullptr)
{
m_target = target;
m_format = sized_format;
m_image_data = data;
m_aspect_flags = aspect_flags;
u32 num_layers;
switch (target)
{
default:
num_layers = 1; break;
case GL_TEXTURE_CUBE_MAP:
num_layers = 6; break;
case GL_TEXTURE_2D_ARRAY:
num_layers = data->depth(); break;
}
glGenTextures(1, &m_id);
glTextureView(m_id, target, data->id(), sized_format, 0, data->levels(), 0, num_layers);
if (argb_swizzle)
{
component_swizzle[0] = argb_swizzle[1];
component_swizzle[1] = argb_swizzle[2];
component_swizzle[2] = argb_swizzle[3];
component_swizzle[3] = argb_swizzle[0];
texture::save_binding_state save(m_target);
glBindTexture(m_target, m_id);
glTexParameteriv(m_target, GL_TEXTURE_SWIZZLE_RGBA, reinterpret_cast<GLint*>(component_swizzle));
}
else
{
component_swizzle[0] = GL_RED;
component_swizzle[1] = GL_GREEN;
component_swizzle[2] = GL_BLUE;
component_swizzle[3] = GL_ALPHA;
}
if (aspect_flags & image_aspect::stencil)
{
constexpr u32 depth_stencil_mask = (image_aspect::depth | image_aspect::stencil);
ensure((aspect_flags & depth_stencil_mask) != depth_stencil_mask); // "Invalid aspect mask combination"
texture::save_binding_state save(m_target);
glBindTexture(m_target, m_id);
glTexParameteri(m_target, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
}
}
public:
texture_view(const texture_view&) = delete;
texture_view(texture_view&&) = delete;
texture_view(texture* data, GLenum target, GLenum sized_format,
const GLenum* argb_swizzle = nullptr,
GLenum aspect_flags = image_aspect::color | image_aspect::depth)
{
create(data, target, sized_format, aspect_flags, argb_swizzle);
}
texture_view(texture* data, const GLenum* argb_swizzle = nullptr,
GLenum aspect_flags = image_aspect::color | image_aspect::depth)
{
GLenum target = static_cast<GLenum>(data->get_target());
GLenum sized_format = static_cast<GLenum>(data->get_internal_format());
create(data, target, sized_format, aspect_flags, argb_swizzle);
}
~texture_view()
{
if (m_id != GL_NONE)
{
glDeleteTextures(1, &m_id);
m_id = GL_NONE;
}
}
GLuint id() const
{
return m_id;
}
GLenum target() const
{
return m_target;
}
GLenum internal_format() const
{
return m_format;
}
GLenum aspect() const
{
return m_aspect_flags;
}
bool compare_swizzle(const GLenum* argb_swizzle) const
{
return (argb_swizzle[0] == component_swizzle[3] &&
argb_swizzle[1] == component_swizzle[0] &&
argb_swizzle[2] == component_swizzle[1] &&
argb_swizzle[3] == component_swizzle[2]);
}
void bind(gl::command_context& cmd, GLuint layer) const
{
cmd->bind_texture(layer, m_target, m_id);
}
texture* image() const
{
return m_image_data;
}
std::array<GLenum, 4> component_mapping() const
{
return{ component_swizzle[3], component_swizzle[0], component_swizzle[1], component_swizzle[2] };
}
u32 encoded_component_map() const
{
// Unused, OGL supports proper component swizzles
return 0u;
}
};
class viewable_image : public texture
{
std::unordered_multimap<u32, std::unique_ptr<texture_view>> views;
public:
using texture::texture;
texture_view* get_view(u32 remap_encoding, const std::pair<std::array<u8, 4>, std::array<u8, 4>>& remap, GLenum aspect_flags = image_aspect::color | image_aspect::depth)
{
auto found = views.equal_range(remap_encoding);
for (auto It = found.first; It != found.second; ++It)
{
if (It->second->aspect() & aspect_flags)
{
return It->second.get();
}
}
ensure(aspect() & aspect_flags);
auto mapping = apply_swizzle_remap(get_native_component_layout(), remap);
auto view = std::make_unique<texture_view>(this, mapping.data(), aspect_flags);
auto result = view.get();
views.emplace(remap_encoding, std::move(view));
return result;
}
void set_native_component_layout(const std::array<GLenum, 4>& layout)
{
if (m_component_layout[0] != layout[0] ||
m_component_layout[1] != layout[1] ||
m_component_layout[2] != layout[2] ||
m_component_layout[3] != layout[3])
{
texture::set_native_component_layout(layout);
views.clear();
}
}
};
class rbo
{
GLuint m_id = GL_NONE;

View File

@ -0,0 +1,33 @@
#pragma once
#include "state_tracker.hpp"
namespace gl
{
static thread_local bool s_tls_primary_context_thread = false;
static gl::driver_state* s_current_state = nullptr;
void set_primary_context_thread(bool value)
{
s_tls_primary_context_thread = value;
}
bool is_primary_context_thread()
{
return s_tls_primary_context_thread;
}
void set_command_context(gl::command_context& ctx)
{
s_current_state = ctx.operator->();
}
void set_command_context(gl::driver_state& ctx)
{
s_current_state = &ctx;
}
gl::command_context get_command_context()
{
return { *s_current_state };
}
}

View File

@ -2,6 +2,26 @@
#include "capabilities.hpp"
#define GL_FRAGMENT_TEXTURES_START 0
#define GL_VERTEX_TEXTURES_START (GL_FRAGMENT_TEXTURES_START + 16)
#define GL_STENCIL_MIRRORS_START (GL_VERTEX_TEXTURES_START + 4)
#define GL_STREAM_BUFFER_START (GL_STENCIL_MIRRORS_START + 16)
#define GL_TEMP_IMAGE_SLOT 31
#define UBO_SLOT(x) (x)
#define SSBO_SLOT(x) (x)
#define GL_VERTEX_PARAMS_BIND_SLOT UBO_SLOT(0)
#define GL_VERTEX_LAYOUT_BIND_SLOT UBO_SLOT(1)
#define GL_VERTEX_CONSTANT_BUFFERS_BIND_SLOT UBO_SLOT(2)
#define GL_FRAGMENT_CONSTANT_BUFFERS_BIND_SLOT UBO_SLOT(3)
#define GL_FRAGMENT_STATE_BIND_SLOT UBO_SLOT(4)
#define GL_FRAGMENT_TEXTURE_PARAMS_BIND_SLOT UBO_SLOT(5)
#define GL_RASTERIZER_STATE_BIND_SLOT UBO_SLOT(6)
#define GL_INTERPRETER_VERTEX_BLOCK SSBO_SLOT(0)
#define GL_INTERPRETER_FRAGMENT_BLOCK SSBO_SLOT(1)
#define GL_COMPUTE_BUFFER_SLOT(index) SSBO_SLOT(2 + index)
//Function call wrapped in ARB_DSA vs EXT_DSA compat check
#define DSA_CALL(func, object_name, target, ...)\
if (::gl::get_driver_caps().ARB_dsa_supported)\

View File

@ -0,0 +1,298 @@
#include "stdafx.h"
#include "image.h"
#include "buffer_object.h"
#include "state_tracker.hpp"
#include "pixel_settings.hpp"
namespace gl
{
texture::texture(GLenum target, GLuint width, GLuint height, GLuint depth, GLuint mipmaps, GLenum sized_format, rsx::format_class format_class)
{
glGenTextures(1, &m_id);
// Must bind to initialize the new texture
gl::get_command_context()->bind_texture(GL_TEMP_IMAGE_SLOT, target, m_id);
switch (target)
{
default:
fmt::throw_exception("Invalid image target 0x%X", target);
case GL_TEXTURE_1D:
glTexStorage1D(target, mipmaps, sized_format, width);
height = depth = 1;
break;
case GL_TEXTURE_2D:
case GL_TEXTURE_CUBE_MAP:
glTexStorage2D(target, mipmaps, sized_format, width, height);
depth = 1;
break;
case GL_TEXTURE_3D:
case GL_TEXTURE_2D_ARRAY:
glTexStorage3D(target, mipmaps, sized_format, width, height, depth);
break;
case GL_TEXTURE_BUFFER:
break;
}
if (target != GL_TEXTURE_BUFFER)
{
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1);
m_width = width;
m_height = height;
m_depth = depth;
m_mipmaps = mipmaps;
m_aspect_flags = image_aspect::color;
switch (sized_format)
{
case GL_DEPTH_COMPONENT16:
{
m_pitch = width * 2;
m_aspect_flags = image_aspect::depth;
break;
}
case GL_DEPTH_COMPONENT32F:
{
m_pitch = width * 4;
m_aspect_flags = image_aspect::depth;
break;
}
case GL_DEPTH24_STENCIL8:
case GL_DEPTH32F_STENCIL8:
{
m_pitch = width * 4;
m_aspect_flags = image_aspect::depth | image_aspect::stencil;
break;
}
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
{
m_compressed = true;
m_pitch = utils::align(width, 4) / 2;
break;
}
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
{
m_compressed = true;
m_pitch = utils::align(width, 4);
break;
}
default:
{
GLenum query_target = (target == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : target;
GLint r, g, b, a;
glGetTexLevelParameteriv(query_target, 0, GL_TEXTURE_RED_SIZE, &r);
glGetTexLevelParameteriv(query_target, 0, GL_TEXTURE_GREEN_SIZE, &g);
glGetTexLevelParameteriv(query_target, 0, GL_TEXTURE_BLUE_SIZE, &b);
glGetTexLevelParameteriv(query_target, 0, GL_TEXTURE_ALPHA_SIZE, &a);
m_pitch = width * (r + g + b + a) / 8;
break;
}
}
if (!m_pitch)
{
fmt::throw_exception("Unhandled GL format 0x%X", sized_format);
}
if (format_class == RSX_FORMAT_CLASS_UNDEFINED)
{
if (m_aspect_flags != image_aspect::color)
{
rsx_log.error("Undefined format class for depth texture is not allowed");
}
else
{
format_class = RSX_FORMAT_CLASS_COLOR;
}
}
}
m_target = static_cast<texture::target>(target);
m_internal_format = static_cast<internal_format>(sized_format);
m_component_layout = { GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE };
m_format_class = format_class;
}
void texture::copy_from(const void* src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings)
{
pixel_settings.apply();
switch (const auto target_ = static_cast<GLenum>(m_target))
{
case GL_TEXTURE_1D:
{
DSA_CALL(TextureSubImage1D, m_id, GL_TEXTURE_1D, level, region.x, region.width, static_cast<GLenum>(format), static_cast<GLenum>(type), src);
break;
}
case GL_TEXTURE_2D:
{
DSA_CALL(TextureSubImage2D, m_id, GL_TEXTURE_2D, level, region.x, region.y, region.width, region.height, static_cast<GLenum>(format), static_cast<GLenum>(type), src);
break;
}
case GL_TEXTURE_3D:
case GL_TEXTURE_2D_ARRAY:
{
DSA_CALL(TextureSubImage3D, m_id, target_, level, region.x, region.y, region.z, region.width, region.height, region.depth, static_cast<GLenum>(format), static_cast<GLenum>(type), src);
break;
}
case GL_TEXTURE_CUBE_MAP:
{
if (get_driver_caps().ARB_dsa_supported)
{
glTextureSubImage3D(m_id, level, region.x, region.y, region.z, region.width, region.height, region.depth, static_cast<GLenum>(format), static_cast<GLenum>(type), src);
}
else
{
rsx_log.warning("Cubemap upload via texture::copy_from is halfplemented!");
auto ptr = static_cast<const u8*>(src);
const auto end = std::min(6u, region.z + region.depth);
for (unsigned face = region.z; face < end; ++face)
{
glTextureSubImage2DEXT(m_id, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, level, region.x, region.y, region.width, region.height, static_cast<GLenum>(format), static_cast<GLenum>(type), ptr);
ptr += (region.width * region.height * 4); //TODO
}
}
break;
}
}
}
void texture::copy_from(buffer& buf, u32 gl_format_type, u32 offset, u32 length)
{
if (get_target() != target::textureBuffer)
fmt::throw_exception("OpenGL error: texture cannot copy from buffer");
DSA_CALL(TextureBufferRange, m_id, GL_TEXTURE_BUFFER, gl_format_type, buf.id(), offset, length);
}
void texture::copy_from(buffer_view& view)
{
copy_from(*view.value(), view.format(), view.offset(), view.range());
}
void texture::copy_to(void* dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const
{
pixel_settings.apply();
const auto& caps = get_driver_caps();
if (!region.x && !region.y && !region.z &&
region.width == m_width && region.height == m_height && region.depth == m_depth)
{
if (caps.ARB_dsa_supported)
glGetTextureImage(m_id, level, static_cast<GLenum>(format), static_cast<GLenum>(type), s32{ smax }, dst);
else
glGetTextureImageEXT(m_id, static_cast<GLenum>(m_target), level, static_cast<GLenum>(format), static_cast<GLenum>(type), dst);
}
else if (caps.ARB_dsa_supported)
{
glGetTextureSubImage(m_id, level, region.x, region.y, region.z, region.width, region.height, region.depth,
static_cast<GLenum>(format), static_cast<GLenum>(type), s32{ smax }, dst);
}
else
{
// Worst case scenario. For some reason, EXT_dsa does not have glGetTextureSubImage
const auto target_ = static_cast<GLenum>(m_target);
texture tmp{ target_, region.width, region.height, region.depth, 1, static_cast<GLenum>(m_internal_format) };
glCopyImageSubData(m_id, target_, level, region.x, region.y, region.z, tmp.id(), target_, 0, 0, 0, 0,
region.width, region.height, region.depth);
const coord3u region2 = { {0, 0, 0}, region.size };
tmp.copy_to(dst, format, type, 0, region2, pixel_settings);
}
}
void texture_view::create(texture* data, GLenum target, GLenum sized_format, GLenum aspect_flags, const GLenum* argb_swizzle)
{
m_target = target;
m_format = sized_format;
m_image_data = data;
m_aspect_flags = aspect_flags;
u32 num_layers;
switch (target)
{
default:
num_layers = 1; break;
case GL_TEXTURE_CUBE_MAP:
num_layers = 6; break;
case GL_TEXTURE_2D_ARRAY:
num_layers = data->depth(); break;
}
glGenTextures(1, &m_id);
glTextureView(m_id, target, data->id(), sized_format, 0, data->levels(), 0, num_layers);
if (argb_swizzle)
{
component_swizzle[0] = argb_swizzle[1];
component_swizzle[1] = argb_swizzle[2];
component_swizzle[2] = argb_swizzle[3];
component_swizzle[3] = argb_swizzle[0];
gl::get_command_context()->bind_texture(GL_TEMP_IMAGE_SLOT, m_target, m_id);
glTexParameteriv(m_target, GL_TEXTURE_SWIZZLE_RGBA, reinterpret_cast<GLint*>(component_swizzle));
}
else
{
component_swizzle[0] = GL_RED;
component_swizzle[1] = GL_GREEN;
component_swizzle[2] = GL_BLUE;
component_swizzle[3] = GL_ALPHA;
}
if (aspect_flags & image_aspect::stencil)
{
constexpr u32 depth_stencil_mask = (image_aspect::depth | image_aspect::stencil);
ensure((aspect_flags & depth_stencil_mask) != depth_stencil_mask); // "Invalid aspect mask combination"
gl::get_command_context()->bind_texture(GL_TEMP_IMAGE_SLOT, m_target, m_id);
glTexParameteri(m_target, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
}
}
void texture_view::bind(gl::command_context& cmd, GLuint layer) const
{
cmd->bind_texture(layer, m_target, m_id);
}
texture_view* viewable_image::get_view(u32 remap_encoding, const std::pair<std::array<u8, 4>, std::array<u8, 4>>& remap, GLenum aspect_flags)
{
auto found = views.equal_range(remap_encoding);
for (auto It = found.first; It != found.second; ++It)
{
if (It->second->aspect() & aspect_flags)
{
return It->second.get();
}
}
ensure(aspect() & aspect_flags);
auto mapping = apply_swizzle_remap(get_native_component_layout(), remap);
auto view = std::make_unique<texture_view>(this, mapping.data(), aspect_flags);
auto result = view.get();
views.emplace(remap_encoding, std::move(view));
return result;
}
void viewable_image::set_native_component_layout(const std::array<GLenum, 4>& layout)
{
if (m_component_layout[0] != layout[0] ||
m_component_layout[1] != layout[1] ||
m_component_layout[2] != layout[2] ||
m_component_layout[3] != layout[3])
{
texture::set_native_component_layout(layout);
views.clear();
}
}
}

View File

@ -0,0 +1,393 @@
#pragma once
#include "common.h"
#include "Utilities/geometry.h"
#include "Emu/RSX/Common/TextureUtils.h"
//using enum rsx::format_class;
using namespace ::rsx::format_class_;
namespace gl
{
class buffer;
class buffer_view;
class command_context;
class pixel_pack_settings;
class pixel_unpack_settings;
enum image_aspect : u32
{
color = 1,
depth = 2,
stencil = 4
};
class texture
{
friend class texture_view;
public:
enum class type
{
ubyte = GL_UNSIGNED_BYTE,
ushort = GL_UNSIGNED_SHORT,
uint = GL_UNSIGNED_INT,
ubyte_3_3_2 = GL_UNSIGNED_BYTE_3_3_2,
ubyte_2_3_3_rev = GL_UNSIGNED_BYTE_2_3_3_REV,
ushort_5_6_5 = GL_UNSIGNED_SHORT_5_6_5,
ushort_5_6_5_rev = GL_UNSIGNED_SHORT_5_6_5_REV,
ushort_4_4_4_4 = GL_UNSIGNED_SHORT_4_4_4_4,
ushort_4_4_4_4_rev = GL_UNSIGNED_SHORT_4_4_4_4_REV,
ushort_5_5_5_1 = GL_UNSIGNED_SHORT_5_5_5_1,
ushort_1_5_5_5_rev = GL_UNSIGNED_SHORT_1_5_5_5_REV,
uint_8_8_8_8 = GL_UNSIGNED_INT_8_8_8_8,
uint_8_8_8_8_rev = GL_UNSIGNED_INT_8_8_8_8_REV,
uint_10_10_10_2 = GL_UNSIGNED_INT_10_10_10_2,
uint_2_10_10_10_rev = GL_UNSIGNED_INT_2_10_10_10_REV,
uint_24_8 = GL_UNSIGNED_INT_24_8,
float32_uint8 = GL_FLOAT_32_UNSIGNED_INT_24_8_REV,
sbyte = GL_BYTE,
sshort = GL_SHORT,
sint = GL_INT,
f16 = GL_HALF_FLOAT,
f32 = GL_FLOAT,
f64 = GL_DOUBLE,
};
enum class channel
{
zero = GL_ZERO,
one = GL_ONE,
r = GL_RED,
g = GL_GREEN,
b = GL_BLUE,
a = GL_ALPHA,
};
enum class format
{
r = GL_RED,
rg = GL_RG,
rgb = GL_RGB,
rgba = GL_RGBA,
bgr = GL_BGR,
bgra = GL_BGRA,
stencil = GL_STENCIL_INDEX,
depth = GL_DEPTH_COMPONENT,
depth_stencil = GL_DEPTH_STENCIL
};
enum class internal_format
{
stencil8 = GL_STENCIL_INDEX8,
depth16 = GL_DEPTH_COMPONENT16,
depth32f = GL_DEPTH_COMPONENT32F,
depth24_stencil8 = GL_DEPTH24_STENCIL8,
depth32f_stencil8 = GL_DEPTH32F_STENCIL8,
compressed_rgb_s3tc_dxt1 = GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
compressed_rgba_s3tc_dxt1 = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
compressed_rgba_s3tc_dxt3 = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
compressed_rgba_s3tc_dxt5 = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
//Sized internal formats, see opengl spec document on glTexImage2D, table 3
rgba8 = GL_RGBA8,
rgb565 = GL_RGB565,
rgb5a1 = GL_RGB5_A1,
rgba4 = GL_RGBA4,
r8 = GL_R8,
r16 = GL_R16,
r32f = GL_R32F,
rg8 = GL_RG8,
rg16 = GL_RG16,
rg16f = GL_RG16F,
rgba16f = GL_RGBA16F,
rgba32f = GL_RGBA32F
};
enum class wrap
{
repeat = GL_REPEAT,
mirrored_repeat = GL_MIRRORED_REPEAT,
clamp_to_edge = GL_CLAMP_TO_EDGE,
clamp_to_border = GL_CLAMP_TO_BORDER,
mirror_clamp = GL_MIRROR_CLAMP_EXT,
//mirror_clamp_to_edge = GL_MIRROR_CLAMP_TO_EDGE,
mirror_clamp_to_border = GL_MIRROR_CLAMP_TO_BORDER_EXT
};
enum class compare_mode
{
none = GL_NONE,
ref_to_texture = GL_COMPARE_REF_TO_TEXTURE
};
enum class target
{
texture1D = GL_TEXTURE_1D,
texture2D = GL_TEXTURE_2D,
texture3D = GL_TEXTURE_3D,
textureCUBE = GL_TEXTURE_CUBE_MAP,
textureBuffer = GL_TEXTURE_BUFFER
};
protected:
GLuint m_id = GL_NONE;
GLuint m_width = 0;
GLuint m_height = 0;
GLuint m_depth = 0;
GLuint m_mipmaps = 0;
GLuint m_pitch = 0;
GLuint m_compressed = GL_FALSE;
GLuint m_aspect_flags = 0;
target m_target = target::texture2D;
internal_format m_internal_format = internal_format::rgba8;
std::array<GLenum, 4> m_component_layout;
rsx::format_class m_format_class = RSX_FORMAT_CLASS_UNDEFINED;
public:
texture(const texture&) = delete;
texture(texture&& texture_) = delete;
texture(GLenum target, GLuint width, GLuint height, GLuint depth, GLuint mipmaps, GLenum sized_format, rsx::format_class format_class = rsx::RSX_FORMAT_CLASS_UNDEFINED);
virtual ~texture()
{
glDeleteTextures(1, &m_id);
m_id = GL_NONE;
}
// Getters/setters
void set_native_component_layout(const std::array<GLenum, 4>& layout)
{
m_component_layout[0] = layout[0];
m_component_layout[1] = layout[1];
m_component_layout[2] = layout[2];
m_component_layout[3] = layout[3];
}
target get_target() const noexcept
{
return m_target;
}
static bool compressed_format(internal_format format_) noexcept
{
switch (format_)
{
case internal_format::compressed_rgb_s3tc_dxt1:
case internal_format::compressed_rgba_s3tc_dxt1:
case internal_format::compressed_rgba_s3tc_dxt3:
case internal_format::compressed_rgba_s3tc_dxt5:
return true;
default:
return false;
}
}
uint id() const noexcept
{
return m_id;
}
explicit operator bool() const noexcept
{
return (m_id != GL_NONE);
}
GLuint width() const
{
return m_width;
}
GLuint height() const
{
return m_height;
}
GLuint depth() const
{
return m_depth;
}
GLuint levels() const
{
return m_mipmaps;
}
GLuint pitch() const
{
return m_pitch;
}
constexpr GLubyte samples() const
{
return 1;
}
GLboolean compressed() const
{
return m_compressed;
}
GLuint aspect() const
{
return m_aspect_flags;
}
rsx::format_class format_class() const
{
return m_format_class;
}
sizeu size2D() const
{
return{ m_width, m_height };
}
size3u size3D() const
{
const auto depth = (m_target == target::textureCUBE) ? 6 : m_depth;
return{ m_width, m_height, depth };
}
texture::internal_format get_internal_format() const
{
return m_internal_format;
}
std::array<GLenum, 4> get_native_component_layout() const
{
return m_component_layout;
}
// Data management
void copy_from(const void* src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings);
void copy_from(buffer& buf, u32 gl_format_type, u32 offset, u32 length);
void copy_from(buffer_view& view);
void copy_to(void* dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const;
// Convenience wrappers
void copy_from(const void* src, texture::format format, texture::type type, const pixel_unpack_settings& pixel_settings)
{
const coord3u region = { {}, size3D() };
copy_from(src, format, type, 0, region, pixel_settings);
}
void copy_to(void* dst, texture::format format, texture::type type, const pixel_pack_settings& pixel_settings) const
{
const coord3u region = { {}, size3D() };
copy_to(dst, format, type, 0, region, pixel_settings);
}
};
class texture_view
{
GLuint m_id = GL_NONE;
GLenum m_target = 0;
GLenum m_format = 0;
GLenum m_aspect_flags = 0;
texture* m_image_data = nullptr;
GLenum component_swizzle[4];
void create(texture* data, GLenum target, GLenum sized_format, GLenum aspect_flags, const GLenum* argb_swizzle = nullptr);
public:
texture_view(const texture_view&) = delete;
texture_view(texture_view&&) = delete;
texture_view(texture* data, GLenum target, GLenum sized_format,
const GLenum* argb_swizzle = nullptr,
GLenum aspect_flags = image_aspect::color | image_aspect::depth)
{
create(data, target, sized_format, aspect_flags, argb_swizzle);
}
texture_view(texture* data, const GLenum* argb_swizzle = nullptr,
GLenum aspect_flags = image_aspect::color | image_aspect::depth)
{
GLenum target = static_cast<GLenum>(data->get_target());
GLenum sized_format = static_cast<GLenum>(data->get_internal_format());
create(data, target, sized_format, aspect_flags, argb_swizzle);
}
virtual ~texture_view()
{
glDeleteTextures(1, &m_id);
m_id = GL_NONE;
}
GLuint id() const
{
return m_id;
}
GLenum target() const
{
return m_target;
}
GLenum internal_format() const
{
return m_format;
}
GLenum aspect() const
{
return m_aspect_flags;
}
bool compare_swizzle(const GLenum* argb_swizzle) const
{
return (argb_swizzle[0] == component_swizzle[3] &&
argb_swizzle[1] == component_swizzle[0] &&
argb_swizzle[2] == component_swizzle[1] &&
argb_swizzle[3] == component_swizzle[2]);
}
texture* image() const
{
return m_image_data;
}
std::array<GLenum, 4> component_mapping() const
{
return{ component_swizzle[3], component_swizzle[0], component_swizzle[1], component_swizzle[2] };
}
u32 encoded_component_map() const
{
// Unused, OGL supports proper component swizzles
return 0u;
}
void bind(gl::command_context& cmd, GLuint layer) const;
};
class viewable_image : public texture
{
std::unordered_multimap<u32, std::unique_ptr<texture_view>> views;
public:
using texture::texture;
texture_view* get_view(u32 remap_encoding, const std::pair<std::array<u8, 4>, std::array<u8, 4>>& remap, GLenum aspect_flags = image_aspect::color | image_aspect::depth);
void set_native_component_layout(const std::array<GLenum, 4>& layout);
};
// Texture helpers
std::array<GLenum, 4> apply_swizzle_remap(const std::array<GLenum, 4>& swizzle_remap, const std::pair<std::array<u8, 4>, std::array<u8, 4>>& decoded_remap);
}

View File

@ -0,0 +1,157 @@
#pragma once
#include "common.h"
namespace gl
{
class pixel_pack_settings
{
bool m_swap_bytes = false;
bool m_lsb_first = false;
int m_row_length = 0;
int m_image_height = 0;
int m_skip_rows = 0;
int m_skip_pixels = 0;
int m_skip_images = 0;
int m_alignment = 4;
public:
void apply() const
{
glPixelStorei(GL_PACK_SWAP_BYTES, m_swap_bytes ? GL_TRUE : GL_FALSE);
glPixelStorei(GL_PACK_LSB_FIRST, m_lsb_first ? GL_TRUE : GL_FALSE);
glPixelStorei(GL_PACK_ROW_LENGTH, m_row_length);
glPixelStorei(GL_PACK_IMAGE_HEIGHT, m_image_height);
glPixelStorei(GL_PACK_SKIP_ROWS, m_skip_rows);
glPixelStorei(GL_PACK_SKIP_PIXELS, m_skip_pixels);
glPixelStorei(GL_PACK_SKIP_IMAGES, m_skip_images);
glPixelStorei(GL_PACK_ALIGNMENT, m_alignment);
}
pixel_pack_settings& swap_bytes(bool value = true)
{
m_swap_bytes = value;
return *this;
}
pixel_pack_settings& lsb_first(bool value = true)
{
m_lsb_first = value;
return *this;
}
pixel_pack_settings& row_length(int value)
{
m_row_length = value;
return *this;
}
pixel_pack_settings& image_height(int value)
{
m_image_height = value;
return *this;
}
pixel_pack_settings& skip_rows(int value)
{
m_skip_rows = value;
return *this;
}
pixel_pack_settings& skip_pixels(int value)
{
m_skip_pixels = value;
return *this;
}
pixel_pack_settings& skip_images(int value)
{
m_skip_images = value;
return *this;
}
pixel_pack_settings& alignment(int value)
{
m_alignment = value;
return *this;
}
bool get_swap_bytes() const
{
return m_swap_bytes;
}
int get_row_length() const
{
return m_row_length;
}
};
class pixel_unpack_settings
{
bool m_swap_bytes = false;
bool m_lsb_first = false;
int m_row_length = 0;
int m_image_height = 0;
int m_skip_rows = 0;
int m_skip_pixels = 0;
int m_skip_images = 0;
int m_alignment = 4;
public:
void apply() const
{
glPixelStorei(GL_UNPACK_SWAP_BYTES, m_swap_bytes ? GL_TRUE : GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST, m_lsb_first ? GL_TRUE : GL_FALSE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, m_row_length);
glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, m_image_height);
glPixelStorei(GL_UNPACK_SKIP_ROWS, m_skip_rows);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, m_skip_pixels);
glPixelStorei(GL_UNPACK_SKIP_IMAGES, m_skip_images);
glPixelStorei(GL_UNPACK_ALIGNMENT, m_alignment);
}
pixel_unpack_settings& swap_bytes(bool value = true)
{
m_swap_bytes = value;
return *this;
}
pixel_unpack_settings& lsb_first(bool value = true)
{
m_lsb_first = value;
return *this;
}
pixel_unpack_settings& row_length(int value)
{
m_row_length = value;
return *this;
}
pixel_unpack_settings& image_height(int value)
{
m_image_height = value;
return *this;
}
pixel_unpack_settings& skip_rows(int value)
{
m_skip_rows = value;
return *this;
}
pixel_unpack_settings& skip_pixels(int value)
{
m_skip_pixels = value;
return *this;
}
pixel_unpack_settings& skip_images(int value)
{
m_skip_images = value;
return *this;
}
pixel_unpack_settings& alignment(int value)
{
m_alignment = value;
return *this;
}
bool get_swap_bytes() const
{
return m_swap_bytes;
}
int get_row_length() const
{
return m_row_length;
}
};
}

View File

@ -1,322 +1,329 @@
#pragma once
#include "glutils/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;
}
}
};
class command_context
{
driver_state* drv;
public:
command_context()
: drv(nullptr)
{}
command_context(driver_state& drv_)
: drv(&drv_)
{}
driver_state* operator -> () {
return drv;
}
};
}
#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;
}
}
};
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();
}

View File

@ -52,7 +52,6 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\GL\GLCompute.h" />
<ClInclude Include="Emu\RSX\GL\GLExecutionState.h" />
<ClInclude Include="Emu\RSX\GL\GLOverlays.h" />
<ClInclude Include="Emu\RSX\GL\GLPipelineCompiler.h" />
<ClInclude Include="Emu\RSX\GL\GLTextOut.h" />
@ -64,7 +63,10 @@
<ClInclude Include="Emu\RSX\GL\glutils\buffer_object.h" />
<ClInclude Include="Emu\RSX\GL\glutils\capabilities.hpp" />
<ClInclude Include="Emu\RSX\GL\glutils\common.h" />
<ClInclude Include="Emu\RSX\GL\glutils\pixel_settings.hpp" />
<ClInclude Include="Emu\RSX\GL\glutils\ring_buffer.h" />
<ClInclude Include="Emu\RSX\GL\glutils\state_tracker.hpp" />
<ClInclude Include="Emu\RSX\GL\glutils\image.h" />
<ClInclude Include="Emu\RSX\GL\GLVertexProgram.h" />
<ClInclude Include="Emu\RSX\GL\GLHelpers.h" />
<ClInclude Include="Emu\RSX\GL\GLRenderTargets.h" />
@ -82,7 +84,9 @@
<ClCompile Include="Emu\RSX\GL\GLOverlays.cpp" />
<ClCompile Include="Emu\RSX\GL\GLPipelineCompiler.cpp" />
<ClCompile Include="Emu\RSX\GL\glutils\buffer_object.cpp" />
<ClCompile Include="Emu\RSX\GL\glutils\common.cpp" />
<ClCompile Include="Emu\RSX\GL\glutils\ring_buffer.cpp" />
<ClCompile Include="Emu\RSX\GL\glutils\image.cpp" />
<ClCompile Include="Emu\RSX\GL\GLVertexProgram.cpp" />
<ClCompile Include="Emu\RSX\GL\GLHelpers.cpp" />
<ClCompile Include="Emu\RSX\GL\GLPresent.cpp" />

View File

@ -23,6 +23,12 @@
<ClCompile Include="Emu\RSX\GL\glutils\ring_buffer.cpp">
<Filter>glutils</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\GL\glutils\image.cpp">
<Filter>glutils</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\GL\glutils\common.cpp">
<Filter>glutils</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\GL\GLTexture.h" />
@ -39,7 +45,6 @@
<ClInclude Include="Emu\RSX\GL\GLShaderInterpreter.h" />
<ClInclude Include="Emu\RSX\GL\GLTextOut.h" />
<ClInclude Include="Emu\RSX\GL\GLOverlays.h" />
<ClInclude Include="Emu\RSX\GL\GLExecutionState.h" />
<ClInclude Include="Emu\RSX\GL\GLCompute.h" />
<ClInclude Include="Emu\RSX\GL\GLPipelineCompiler.h" />
<ClInclude Include="Emu\RSX\GL\glutils\buffer_object.h">
@ -54,6 +59,15 @@
<ClInclude Include="Emu\RSX\GL\glutils\capabilities.hpp">
<Filter>glutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\GL\glutils\image.h">
<Filter>glutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\GL\glutils\state_tracker.hpp">
<Filter>glutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\GL\glutils\pixel_settings.hpp">
<Filter>glutils</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="glutils">