1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 02:32:36 +01:00

gl: Refactor the rest of GLHelpers

This commit is contained in:
kd-11 2022-06-05 18:21:59 +03:00 committed by kd-11
parent 09824a718f
commit 35ef19cfc8
28 changed files with 1806 additions and 1817 deletions

View File

@ -700,6 +700,12 @@ template<typename T>
struct size3_base
{
T width, height, depth;
template<typename NT>
explicit constexpr operator size3_base<NT>() const
{
return{ static_cast<NT>(width), static_cast<NT>(height), static_cast<NT>(depth) };
}
};
template<typename T>

View File

@ -2,6 +2,7 @@
#include "Emu/IdManager.h"
#include "GLHelpers.h"
#include "glutils/program.h"
#include "../rsx_utils.h"
#include <unordered_map>

View File

@ -2,6 +2,7 @@
#include "../Program/FragmentProgramDecompiler.h"
#include "../Program/GLSLTypes.h"
#include "GLHelpers.h"
#include "glutils/program.h"
namespace glsl
{

View File

@ -10,9 +10,7 @@
namespace gl
{
std::unordered_map<u32, std::unique_ptr<gl::compute_task>> g_compute_tasks;
blitter *g_hw_blitter = nullptr;
capabilities g_driver_caps;
const fbo screen{};
void flush_command_queue(fence& fence_obj)
{
@ -171,271 +169,6 @@ namespace gl
return g_driver_caps;
}
void fbo::create()
{
glGenFramebuffers(1, &m_id);
}
void fbo::bind() const
{
glBindFramebuffer(GL_FRAMEBUFFER, m_id);
}
void fbo::blit(const fbo& dst, areai src_area, areai dst_area, buffers buffers_, filter filter_) const
{
bind_as(target::read_frame_buffer);
dst.bind_as(target::draw_frame_buffer);
glBlitFramebuffer(
src_area.x1, src_area.y1, src_area.x2, src_area.y2,
dst_area.x1, dst_area.y1, dst_area.x2, dst_area.y2,
static_cast<GLbitfield>(buffers_), static_cast<GLenum>(filter_));
}
void fbo::bind_as(target target_) const
{
glBindFramebuffer(static_cast<int>(target_), id());
}
void fbo::remove()
{
if (m_id != GL_NONE)
{
glDeleteFramebuffers(1, &m_id);
m_id = GL_NONE;
}
}
bool fbo::created() const
{
return m_id != GL_NONE;
}
bool fbo::check() const
{
GLenum status = DSA_CALL2_RET(CheckNamedFramebufferStatus, m_id, GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
rsx_log.error("FBO check failed: 0x%04x", status);
return false;
}
return true;
}
void fbo::recreate()
{
if (created())
remove();
create();
}
void fbo::draw_buffer(const attachment& buffer) const
{
GLenum buf = buffer.id();
DSA_CALL3(NamedFramebufferDrawBuffers, FramebufferDrawBuffers, m_id, 1, &buf);
}
void fbo::draw_buffers(const std::initializer_list<attachment>& indexes) const
{
rsx::simple_array<GLenum> ids;
ids.reserve(::size32(indexes));
for (auto &index : indexes)
ids.push_back(index.id());
DSA_CALL3(NamedFramebufferDrawBuffers, FramebufferDrawBuffers, m_id, static_cast<GLsizei>(ids.size()), ids.data());
}
void fbo::read_buffer(const attachment& buffer) const
{
DSA_CALL3(NamedFramebufferReadBuffer, FramebufferReadBuffer, m_id, buffer.id());
}
void fbo::draw_arrays(rsx::primitive_type mode, GLsizei count, GLint first) const
{
save_binding_state save(*this);
glDrawArrays(draw_mode(mode), first, count);
}
void fbo::draw_arrays(const buffer& buffer, rsx::primitive_type mode, GLsizei count, GLint first) const
{
buffer.bind(buffer::target::array);
draw_arrays(mode, count, first);
}
void fbo::draw_arrays(const vao& buffer, rsx::primitive_type mode, GLsizei count, GLint first) const
{
buffer.bind();
draw_arrays(mode, count, first);
}
void fbo::draw_elements(rsx::primitive_type mode, GLsizei count, indices_type type, const GLvoid *indices) const
{
save_binding_state save(*this);
glDrawElements(draw_mode(mode), count, static_cast<GLenum>(type), indices);
}
void fbo::draw_elements(const buffer& buffer, rsx::primitive_type mode, GLsizei count, indices_type type, const GLvoid *indices) const
{
buffer.bind(buffer::target::array);
glDrawElements(draw_mode(mode), count, static_cast<GLenum>(type), indices);
}
void fbo::draw_elements(rsx::primitive_type mode, GLsizei count, indices_type type, const buffer& indices, usz indices_buffer_offset) const
{
indices.bind(buffer::target::element_array);
glDrawElements(draw_mode(mode), count, static_cast<GLenum>(type), reinterpret_cast<GLvoid*>(indices_buffer_offset));
}
void fbo::draw_elements(const buffer& buffer_, rsx::primitive_type mode, GLsizei count, indices_type type, const buffer& indices, usz indices_buffer_offset) const
{
buffer_.bind(buffer::target::array);
draw_elements(mode, count, type, indices, indices_buffer_offset);
}
void fbo::draw_elements(rsx::primitive_type mode, GLsizei count, const GLubyte *indices) const
{
draw_elements(mode, count, indices_type::ubyte, indices);
}
void fbo::draw_elements(const buffer& buffer, rsx::primitive_type mode, GLsizei count, const GLubyte *indices) const
{
draw_elements(buffer, mode, count, indices_type::ubyte, indices);
}
void fbo::draw_elements(rsx::primitive_type mode, GLsizei count, const GLushort *indices) const
{
draw_elements(mode, count, indices_type::ushort, indices);
}
void fbo::draw_elements(const buffer& buffer, rsx::primitive_type mode, GLsizei count, const GLushort *indices) const
{
draw_elements(buffer, mode, count, indices_type::ushort, indices);
}
void fbo::draw_elements(rsx::primitive_type mode, GLsizei count, const GLuint *indices) const
{
draw_elements(mode, count, indices_type::uint, indices);
}
void fbo::draw_elements(const buffer& buffer, rsx::primitive_type mode, GLsizei count, const GLuint *indices) const
{
draw_elements(buffer, mode, count, indices_type::uint, indices);
}
void fbo::clear(buffers buffers_) const
{
save_binding_state save(*this);
glClear(static_cast<GLbitfield>(buffers_));
}
void fbo::clear(buffers buffers_, color4f color_value, double depth_value, u8 stencil_value) const
{
save_binding_state save(*this);
glClearColor(color_value.r, color_value.g, color_value.b, color_value.a);
glClearDepth(depth_value);
glClearStencil(stencil_value);
clear(buffers_);
}
void fbo::copy_from(const void* pixels, const sizei& size, gl::texture::format format_, gl::texture::type type_, class pixel_unpack_settings pixel_settings) const
{
save_binding_state save(*this);
pixel_settings.apply();
glDrawPixels(size.width, size.height, static_cast<GLenum>(format_), static_cast<GLenum>(type_), pixels);
}
void fbo::copy_from(const buffer& buf, const sizei& size, gl::texture::format format_, gl::texture::type type_, class pixel_unpack_settings pixel_settings) const
{
save_binding_state save(*this);
buffer::save_binding_state save_buffer(buffer::target::pixel_unpack, buf);
pixel_settings.apply();
glDrawPixels(size.width, size.height, static_cast<GLenum>(format_), static_cast<GLenum>(type_), nullptr);
}
void fbo::copy_to(void* pixels, coordi coord, gl::texture::format format_, gl::texture::type type_, class pixel_pack_settings pixel_settings) const
{
save_binding_state save(*this);
pixel_settings.apply();
glReadPixels(coord.x, coord.y, coord.width, coord.height, static_cast<GLenum>(format_), static_cast<GLenum>(type_), pixels);
}
void fbo::copy_to(const buffer& buf, coordi coord, gl::texture::format format_, gl::texture::type type_, class pixel_pack_settings pixel_settings) const
{
save_binding_state save(*this);
buffer::save_binding_state save_buffer(buffer::target::pixel_pack, buf);
pixel_settings.apply();
glReadPixels(coord.x, coord.y, coord.width, coord.height, static_cast<GLenum>(format_), static_cast<GLenum>(type_), nullptr);
}
fbo fbo::get_bound_draw_buffer()
{
GLint value;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &value);
return{ static_cast<GLuint>(value) };
}
fbo fbo::get_bound_read_buffer()
{
GLint value;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &value);
return{ static_cast<GLuint>(value) };
}
fbo fbo::get_bound_buffer()
{
GLint value;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &value);
return{ static_cast<GLuint>(value) };
}
GLuint fbo::id() const
{
return m_id;
}
void fbo::set_id(GLuint id)
{
m_id = id;
}
void fbo::set_extents(const size2i& extents)
{
m_size = extents;
}
size2i fbo::get_extents() const
{
return m_size;
}
bool fbo::matches(const std::array<GLuint, 4>& color_targets, GLuint depth_stencil_target) const
{
for (u32 index = 0; index < 4; ++index)
{
if (color[index].resource_id() != color_targets[index])
{
return false;
}
}
const auto depth_resource = depth.resource_id() | depth_stencil.resource_id();
return (depth_resource == depth_stencil_target);
}
bool fbo::references_any(const std::vector<GLuint>& resources) const
{
return std::any_of(m_resource_bindings.cbegin(), m_resource_bindings.cend(), [&resources](const auto& e)
{
return std::find(resources.cbegin(), resources.cend(), e.second) != resources.cend();
});
}
bool is_primitive_native(rsx::primitive_type in)
{
switch (in)
@ -456,201 +189,4 @@ namespace gl
fmt::throw_exception("unknown primitive type");
}
}
attrib_t vao::operator[](u32 index) const noexcept
{
return attrib_t(index);
}
void blitter::scale_image(gl::command_context& cmd, const texture* src, texture* dst, areai src_rect, areai dst_rect,
bool linear_interpolation, const rsx::typeless_xfer& xfer_info)
{
std::unique_ptr<texture> typeless_src;
std::unique_ptr<texture> typeless_dst;
const gl::texture* real_src = src;
const gl::texture* real_dst = dst;
// Optimization pass; check for pass-through data transfer
if (!xfer_info.flip_horizontal && !xfer_info.flip_vertical && src_rect.height() == dst_rect.height())
{
auto src_w = src_rect.width();
auto dst_w = dst_rect.width();
if (xfer_info.src_is_typeless) src_w = static_cast<int>(src_w * xfer_info.src_scaling_hint);
if (xfer_info.dst_is_typeless) dst_w = static_cast<int>(dst_w * xfer_info.dst_scaling_hint);
if (src_w == dst_w)
{
// Final dimensions are a match
if (xfer_info.src_is_typeless || xfer_info.dst_is_typeless)
{
const coord3i src_region = { { src_rect.x1, src_rect.y1, 0 }, { src_rect.width(), src_rect.height(), 1 } };
const coord3i dst_region = { { dst_rect.x1, dst_rect.y1, 0 }, { dst_rect.width(), dst_rect.height(), 1 } };
gl::copy_typeless(cmd, dst, src, static_cast<coord3u>(dst_region), static_cast<coord3u>(src_region));
}
else
{
glCopyImageSubData(src->id(), GL_TEXTURE_2D, 0, src_rect.x1, src_rect.y1, 0,
dst->id(), GL_TEXTURE_2D, 0, dst_rect.x1, dst_rect.y1, 0,
src_rect.width(), src_rect.height(), 1);
}
return;
}
}
if (xfer_info.src_is_typeless)
{
const auto internal_fmt = xfer_info.src_native_format_override ?
GLenum(xfer_info.src_native_format_override) :
get_sized_internal_format(xfer_info.src_gcm_format);
if (static_cast<gl::texture::internal_format>(internal_fmt) != src->get_internal_format())
{
const u16 internal_width = static_cast<u16>(src->width() * xfer_info.src_scaling_hint);
typeless_src = std::make_unique<texture>(GL_TEXTURE_2D, internal_width, src->height(), 1, 1, internal_fmt);
copy_typeless(cmd, typeless_src.get(), src);
real_src = typeless_src.get();
src_rect.x1 = static_cast<u16>(src_rect.x1 * xfer_info.src_scaling_hint);
src_rect.x2 = static_cast<u16>(src_rect.x2 * xfer_info.src_scaling_hint);
}
}
if (xfer_info.dst_is_typeless)
{
const auto internal_fmt = xfer_info.dst_native_format_override ?
GLenum(xfer_info.dst_native_format_override) :
get_sized_internal_format(xfer_info.dst_gcm_format);
if (static_cast<gl::texture::internal_format>(internal_fmt) != dst->get_internal_format())
{
const auto internal_width = static_cast<u16>(dst->width() * xfer_info.dst_scaling_hint);
typeless_dst = std::make_unique<texture>(GL_TEXTURE_2D, internal_width, dst->height(), 1, 1, internal_fmt);
copy_typeless(cmd, typeless_dst.get(), dst);
real_dst = typeless_dst.get();
dst_rect.x1 = static_cast<u16>(dst_rect.x1 * xfer_info.dst_scaling_hint);
dst_rect.x2 = static_cast<u16>(dst_rect.x2 * xfer_info.dst_scaling_hint);
}
}
ensure(real_src->aspect() == real_dst->aspect());
if (src_rect.width() == dst_rect.width() && src_rect.height() == dst_rect.height() &&
!src_rect.is_flipped() && !dst_rect.is_flipped())
{
glCopyImageSubData(real_src->id(), static_cast<GLenum>(real_src->get_target()), 0, src_rect.x1, src_rect.y1, 0,
real_dst->id(), static_cast<GLenum>(real_dst->get_target()), 0, dst_rect.x1, dst_rect.y1, 0,
src_rect.width(), src_rect.height(), 1);
}
else
{
const bool is_depth_copy = (real_src->aspect() != image_aspect::color);
const filter interp = (linear_interpolation && !is_depth_copy) ? filter::linear : filter::nearest;
gl::fbo::attachment::type attachment;
gl::buffers target;
if (is_depth_copy)
{
if (real_dst->aspect() & gl::image_aspect::stencil)
{
attachment = fbo::attachment::type::depth_stencil;
target = gl::buffers::depth_stencil;
}
else
{
attachment = fbo::attachment::type::depth;
target = gl::buffers::depth;
}
}
else
{
attachment = fbo::attachment::type::color;
target = gl::buffers::color;
}
cmd->disable(GL_SCISSOR_TEST);
save_binding_state saved;
gl::fbo::attachment src_att{blit_src, static_cast<fbo::attachment::type>(attachment)};
src_att = *real_src;
gl::fbo::attachment dst_att{ blit_dst, static_cast<fbo::attachment::type>(attachment) };
dst_att = *real_dst;
if (xfer_info.flip_horizontal)
{
src_rect.flip_horizontal();
}
if (xfer_info.flip_vertical)
{
src_rect.flip_vertical();
}
blit_src.blit(blit_dst, src_rect, dst_rect, target, interp);
// Release the attachments explicitly (not doing so causes glitches, e.g Journey Menu)
src_att = GL_NONE;
dst_att = GL_NONE;
}
if (xfer_info.dst_is_typeless)
{
// Transfer contents from typeless dst back to original dst
copy_typeless(cmd, dst, typeless_dst.get());
}
}
void blitter::fast_clear_image(gl::command_context& cmd, const texture* dst, const color4f& color)
{
save_binding_state saved;
blit_dst.bind();
blit_dst.color[0] = *dst;
blit_dst.check();
cmd->clear_color(color);
cmd->color_maski(0, true, true, true, true);
glClear(GL_COLOR_BUFFER_BIT);
blit_dst.color[0] = GL_NONE;
}
void blitter::fast_clear_image(gl::command_context& cmd, const texture* dst, float /*depth*/, u8 /*stencil*/)
{
fbo::attachment::type attachment;
GLbitfield clear_mask;
switch (const auto fmt = dst->get_internal_format())
{
case texture::internal_format::depth16:
case texture::internal_format::depth32f:
clear_mask = GL_DEPTH_BUFFER_BIT;
attachment = fbo::attachment::type::depth;
break;
case texture::internal_format::depth24_stencil8:
case texture::internal_format::depth32f_stencil8:
clear_mask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
attachment = fbo::attachment::type::depth_stencil;
break;
default:
fmt::throw_exception("Invalid texture passed to clear depth function, format=0x%x", static_cast<u32>(fmt));
}
save_binding_state saved;
fbo::attachment attach_point{ blit_dst, attachment };
blit_dst.bind();
attach_point = *dst;
blit_dst.check();
cmd->depth_mask(GL_TRUE);
cmd->stencil_mask(0xFF);
glClear(clear_mask);
attach_point = GL_NONE;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,11 @@
#include "../Common/simple_array.hpp"
#include "../Overlays/overlays.h"
#include "GLTexture.h"
#include "glutils/fbo.h"
#include "glutils/program.h"
#include "glutils/vao.hpp"
#include <string>
#include <unordered_map>
@ -115,6 +120,16 @@ namespace gl
void run(gl::command_context& cmd, const buffer* src, const texture* dst, const u32 src_offset, const coordu& dst_region, const pixel_unpack_settings& settings);
};
struct rp_copy_rgba_to_bgra : public overlay_pass
{
void run(gl::command_context& cmd, const texture* src, const texture* dst, const coordu& src_region, const coordu& dst_region);
};
struct rp_copy_bgra_to_rgba : public overlay_pass
{
void run(gl::command_context& cmd, const texture* src, const texture* dst, const coordu& src_region, const coordu& dst_region);
};
// TODO: Replace with a proper manager
extern std::unordered_map<u32, std::unique_ptr<gl::overlay_pass>> g_overlay_passes;

View File

@ -1,5 +1,6 @@
#pragma once
#include "GLHelpers.h"
#include "glutils/program.h"
#include "Emu/RSX/display.h"
#include "Utilities/lockless.h"

View File

@ -3,6 +3,8 @@
#include "GLHelpers.h"
#include "../rsx_utils.h"
#include "glutils/fbo.h"
struct color_swizzle
{
gl::texture::channel a = gl::texture::channel::a;

View File

@ -1,5 +1,6 @@
#pragma once
#include "GLHelpers.h"
#include "glutils/program.h"
#include "../Program/ProgramStateCache.h"
#include "../Common/TextureUtils.h"

View File

@ -2,6 +2,7 @@
#include "util/types.hpp"
#include "GLHelpers.h"
#include "glutils/vao.hpp"
#include "../Common/TextGlyphs.h"
#include <string>
#include <vector>

View File

@ -1,8 +1,9 @@
#pragma once
#include "util/types.hpp"
#include "GLRenderTargets.h"
#include "glutils/blitter.h"
#include "glutils/sync.hpp"
#include "../Common/texture_cache.h"
#include <memory>

View File

@ -1,6 +1,7 @@
#pragma once
#include "../Program/VertexProgramDecompiler.h"
#include "GLHelpers.h"
#include "glutils/program.h"
#include <unordered_map>

View File

@ -0,0 +1,268 @@
#include "stdafx.h"
#include "blitter.h"
#include "state_tracker.hpp"
#include "../GLTexture.h" // TODO: This system also needs to be refactored
#include "../GLOverlays.h"
namespace gl
{
static blitter* g_hw_blitter = nullptr;
void process_bgra_transfer_source(const gl::texture* src, const gl::texture* dst, const coord3i& region)
{
ensure(src->get_internal_format() == texture::internal_format::bgra8);
ensure(dst->get_internal_format() == texture::internal_format::rgba8);
}
void process_bgra_transfer_dest(const gl::texture* tex, const coord3i& region)
{
ensure(tex->get_internal_format() == texture::internal_format::bgra8);
}
void blitter::copy_image(gl::command_context& cmd, const texture* src, const texture* dst, int src_level, int dst_level, const position3i& src_offset, const position3i& dst_offset, const size3i& size) const
{
ensure(src_level == 0);
// Typeless bypass for BGRA8
std::unique_ptr<gl::texture> temp_image;
const texture* real_src = src;
bool handle_bgra8_dest = false;
if (src->get_internal_format() != dst->get_internal_format())
{
if (false && src->get_internal_format() == texture::internal_format::bgra8)
{
temp_image = std::make_unique<texture>(static_cast<GLenum>(src->get_target()), src->width(), src->height(), src->depth(), src->levels(), GL_RGBA8, rsx::format_class::RSX_FORMAT_CLASS_COLOR);
process_bgra_transfer_source(src, temp_image.get(), { src_offset, size });
real_src = temp_image.get();
}
handle_bgra8_dest = (dst->get_internal_format() == texture::internal_format::bgra8);
}
glCopyImageSubData(real_src->id(), static_cast<GLenum>(real_src->get_target()), src_level,
src_offset.x, src_offset.y, src_offset.z,
dst->id(), static_cast<GLenum>(dst->get_target()), dst_level,
dst_offset.x, dst_offset.y, dst_offset.z, size.width, size.height, size.depth);
if (handle_bgra8_dest)
{
process_bgra_transfer_dest(dst, { dst_offset, size });
}
}
void blitter::scale_image(gl::command_context& cmd, const texture* src, texture* dst, areai src_rect, areai dst_rect,
bool linear_interpolation, const rsx::typeless_xfer& xfer_info)
{
std::unique_ptr<texture> typeless_src;
std::unique_ptr<texture> typeless_dst;
const gl::texture* real_src = src;
const gl::texture* real_dst = dst;
// Optimization pass; check for pass-through data transfer
if (!xfer_info.flip_horizontal && !xfer_info.flip_vertical && src_rect.height() == dst_rect.height())
{
auto src_w = src_rect.width();
auto dst_w = dst_rect.width();
if (xfer_info.src_is_typeless) src_w = static_cast<int>(src_w * xfer_info.src_scaling_hint);
if (xfer_info.dst_is_typeless) dst_w = static_cast<int>(dst_w * xfer_info.dst_scaling_hint);
if (src_w == dst_w)
{
// Final dimensions are a match
if (xfer_info.src_is_typeless || xfer_info.dst_is_typeless)
{
const coord3i src_region = { { src_rect.x1, src_rect.y1, 0 }, { src_rect.width(), src_rect.height(), 1 } };
const coord3i dst_region = { { dst_rect.x1, dst_rect.y1, 0 }, { dst_rect.width(), dst_rect.height(), 1 } };
gl::copy_typeless(cmd, dst, src, static_cast<coord3u>(dst_region), static_cast<coord3u>(src_region));
}
else
{
copy_image(cmd, src, dst, 0, 1, { src_rect.x1, src_rect.y1, 0u }, { dst_rect.x1, dst_rect.y1, 0 }, { src_rect.width(), src_rect.height(), 1 });
}
return;
}
}
if (xfer_info.src_is_typeless)
{
const auto internal_fmt = xfer_info.src_native_format_override ?
GLenum(xfer_info.src_native_format_override) :
get_sized_internal_format(xfer_info.src_gcm_format);
if (static_cast<gl::texture::internal_format>(internal_fmt) != src->get_internal_format())
{
const u16 internal_width = static_cast<u16>(src->width() * xfer_info.src_scaling_hint);
typeless_src = std::make_unique<texture>(GL_TEXTURE_2D, internal_width, src->height(), 1, 1, internal_fmt);
copy_typeless(cmd, typeless_src.get(), src);
real_src = typeless_src.get();
src_rect.x1 = static_cast<u16>(src_rect.x1 * xfer_info.src_scaling_hint);
src_rect.x2 = static_cast<u16>(src_rect.x2 * xfer_info.src_scaling_hint);
}
}
if (xfer_info.dst_is_typeless)
{
const auto internal_fmt = xfer_info.dst_native_format_override ?
GLenum(xfer_info.dst_native_format_override) :
get_sized_internal_format(xfer_info.dst_gcm_format);
if (static_cast<gl::texture::internal_format>(internal_fmt) != dst->get_internal_format())
{
const auto internal_width = static_cast<u16>(dst->width() * xfer_info.dst_scaling_hint);
typeless_dst = std::make_unique<texture>(GL_TEXTURE_2D, internal_width, dst->height(), 1, 1, internal_fmt);
copy_typeless(cmd, typeless_dst.get(), dst);
real_dst = typeless_dst.get();
dst_rect.x1 = static_cast<u16>(dst_rect.x1 * xfer_info.dst_scaling_hint);
dst_rect.x2 = static_cast<u16>(dst_rect.x2 * xfer_info.dst_scaling_hint);
}
}
if (src->get_internal_format() == texture::internal_format::bgra8 &&
real_src == src &&
dst->get_internal_format() != src->get_internal_format())
{
// Not typeless, plus src is bgra8. Needs conversion
typeless_src = std::make_unique<texture>(GL_TEXTURE_2D, src->width(), src->height(), 1, 1, GL_RGBA8);
process_bgra_transfer_source(src, typeless_src.get(), {{src_rect.x1, src_rect.y1, 0}, {src_rect.width(), src_rect.height(), 1}});
real_src = typeless_src.get();
}
bool handle_bgra8_dest = false;
if (dst->get_internal_format() == texture::internal_format::bgra8 &&
real_dst == dst &&
dst->get_internal_format() != src->get_internal_format())
{
// Not typeless but dst is bgra8.
// Handle the conversion in post
handle_bgra8_dest = true;
}
ensure(real_src->aspect() == real_dst->aspect());
if (src_rect.width() == dst_rect.width() && src_rect.height() == dst_rect.height() &&
!src_rect.is_flipped() && !dst_rect.is_flipped())
{
copy_image(cmd, real_src, real_dst, 0, 1, { src_rect.x1, src_rect.y1, 0 }, { dst_rect.x1, dst_rect.y1, 0 }, { src_rect.width(), src_rect.height(), 1 });
handle_bgra8_dest = false; // Handled in copy_image
}
else
{
const bool is_depth_copy = (real_src->aspect() != image_aspect::color);
const filter interp = (linear_interpolation && !is_depth_copy) ? filter::linear : filter::nearest;
gl::fbo::attachment::type attachment;
gl::buffers target;
if (is_depth_copy)
{
if (real_dst->aspect() & gl::image_aspect::stencil)
{
attachment = fbo::attachment::type::depth_stencil;
target = gl::buffers::depth_stencil;
}
else
{
attachment = fbo::attachment::type::depth;
target = gl::buffers::depth;
}
}
else
{
attachment = fbo::attachment::type::color;
target = gl::buffers::color;
}
cmd->disable(GL_SCISSOR_TEST);
save_binding_state saved;
gl::fbo::attachment src_att{ blit_src, static_cast<fbo::attachment::type>(attachment) };
src_att = *real_src;
gl::fbo::attachment dst_att{ blit_dst, static_cast<fbo::attachment::type>(attachment) };
dst_att = *real_dst;
if (xfer_info.flip_horizontal)
{
src_rect.flip_horizontal();
}
if (xfer_info.flip_vertical)
{
src_rect.flip_vertical();
}
blit_src.blit(blit_dst, src_rect, dst_rect, target, interp);
// Release the attachments explicitly (not doing so causes glitches, e.g Journey Menu)
src_att = GL_NONE;
dst_att = GL_NONE;
}
if (xfer_info.dst_is_typeless)
{
// Transfer contents from typeless dst back to original dst
copy_typeless(cmd, dst, typeless_dst.get());
}
else if (handle_bgra8_dest)
{
// Blit transfer to BGRA8 target
process_bgra_transfer_dest(dst, { {dst_rect.x1, dst_rect.y1, 0}, {dst_rect.width(), dst_rect.height(), 1} });
}
}
void blitter::fast_clear_image(gl::command_context& cmd, const texture* dst, const color4f& color)
{
save_binding_state saved;
blit_dst.bind();
blit_dst.color[0] = *dst;
blit_dst.check();
cmd->clear_color(color);
cmd->color_maski(0, true, true, true, true);
glClear(GL_COLOR_BUFFER_BIT);
blit_dst.color[0] = GL_NONE;
}
void blitter::fast_clear_image(gl::command_context& cmd, const texture* dst, float /*depth*/, u8 /*stencil*/)
{
fbo::attachment::type attachment;
GLbitfield clear_mask;
switch (const auto fmt = dst->get_internal_format())
{
case texture::internal_format::depth16:
case texture::internal_format::depth32f:
clear_mask = GL_DEPTH_BUFFER_BIT;
attachment = fbo::attachment::type::depth;
break;
case texture::internal_format::depth24_stencil8:
case texture::internal_format::depth32f_stencil8:
clear_mask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
attachment = fbo::attachment::type::depth_stencil;
break;
default:
fmt::throw_exception("Invalid texture passed to clear depth function, format=0x%x", static_cast<u32>(fmt));
}
save_binding_state saved;
fbo::attachment attach_point{ blit_dst, attachment };
blit_dst.bind();
attach_point = *dst;
blit_dst.check();
cmd->depth_mask(GL_TRUE);
cmd->stencil_mask(0xFF);
glClear(clear_mask);
attach_point = GL_NONE;
}
}

View File

@ -0,0 +1,58 @@
#pragma once
#include "common.h"
#include "fbo.h"
namespace gl
{
class command_context;
class texture;
class blitter
{
struct save_binding_state
{
GLuint old_fbo;
save_binding_state()
{
glGetIntegerv(GL_FRAMEBUFFER_BINDING, reinterpret_cast<GLint*>(&old_fbo));
}
~save_binding_state()
{
glBindFramebuffer(GL_FRAMEBUFFER, old_fbo);
}
};
fbo blit_src;
fbo blit_dst;
public:
void init()
{
blit_src.create();
blit_dst.create();
}
void destroy()
{
blit_dst.remove();
blit_src.remove();
}
void scale_image(gl::command_context& cmd, const texture* src, texture* dst, areai src_rect, areai dst_rect, bool linear_interpolation,
const rsx::typeless_xfer& xfer_info);
void copy_image(gl::command_context& cmd, const texture* src, const texture* dst, int src_level, int dst_level, const position3i& src_offset, const position3i& dst_offset, const size3i& size) const;
void fast_clear_image(gl::command_context& cmd, const texture* dst, const color4f& color);
void fast_clear_image(gl::command_context& cmd, const texture* dst, float depth, u8 stencil);
void copy_image(gl::command_context& cmd, const texture* src, const texture* dst, int src_level, int dst_level, const position3u& src_offset, const position3u& dst_offset, const size3u& size) const
{
copy_image(cmd, src, dst, src_level, dst_level, static_cast<position3i>(src_offset), static_cast<position3i>(dst_offset), static_cast<size3i>(size));
}
};
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "state_tracker.hpp"
#include "vao.hpp"
namespace gl
{
@ -30,4 +31,9 @@ namespace gl
{
return { *s_current_state };
}
attrib_t vao::operator[](u32 index) const noexcept
{
return attrib_t(index);
}
}

View File

@ -48,127 +48,25 @@
namespace gl
{
// TODO: Move to sync.h
class fence
template<typename Type, uint BindId, uint GetStateId>
class save_binding_state_base
{
GLsync m_value = nullptr;
mutable GLenum flags = GL_SYNC_FLUSH_COMMANDS_BIT;
mutable bool signaled = false;
GLint m_last_binding;
public:
fence() = default;
~fence() = default;
void create()
save_binding_state_base(const Type& new_state) : save_binding_state_base()
{
m_value = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
flags = GL_SYNC_FLUSH_COMMANDS_BIT;
new_state.bind();
}
void destroy()
save_binding_state_base()
{
glDeleteSync(m_value);
m_value = nullptr;
glGetIntegerv(GetStateId, &m_last_binding);
}
void reset()
~save_binding_state_base()
{
if (m_value != nullptr)
destroy();
create();
}
bool is_empty() const
{
return (m_value == nullptr);
}
bool check_signaled() const
{
ensure(m_value);
if (signaled)
return true;
if (flags)
{
GLenum err = glClientWaitSync(m_value, flags, 0);
flags = 0;
if (!(err == GL_ALREADY_SIGNALED || err == GL_CONDITION_SATISFIED))
return false;
}
else
{
GLint status = GL_UNSIGNALED;
GLint tmp;
glGetSynciv(m_value, GL_SYNC_STATUS, 4, &tmp, &status);
if (status != GL_SIGNALED)
return false;
}
signaled = true;
return true;
}
bool wait_for_signal()
{
ensure(m_value);
if (signaled == GL_FALSE)
{
GLenum err = GL_WAIT_FAILED;
bool done = false;
while (!done)
{
if (flags)
{
err = glClientWaitSync(m_value, flags, 0);
flags = 0;
switch (err)
{
default:
rsx_log.error("gl::fence sync returned unknown error 0x%X", err);
[[fallthrough]];
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:
done = true;
break;
case GL_TIMEOUT_EXPIRED:
continue;
}
}
else
{
GLint status = GL_UNSIGNALED;
GLint tmp;
glGetSynciv(m_value, GL_SYNC_STATUS, 4, &tmp, &status);
if (status == GL_SIGNALED)
break;
}
}
signaled = (err == GL_ALREADY_SIGNALED || err == GL_CONDITION_SATISFIED);
}
glDeleteSync(m_value);
m_value = nullptr;
return signaled;
}
void server_wait_sync() const
{
ensure(m_value != nullptr);
glWaitSync(m_value, 0, GL_TIMEOUT_IGNORED);
glBindBuffer(BindId, m_last_binding);
}
};
}

View File

@ -0,0 +1,276 @@
#include "stdafx.h"
#include "fbo.h"
#include "buffer_object.h"
#include "vao.hpp"
#include "Emu/RSX/Common/simple_array.hpp"
namespace gl
{
const fbo screen{};
void fbo::create()
{
glGenFramebuffers(1, &m_id);
}
void fbo::bind() const
{
glBindFramebuffer(GL_FRAMEBUFFER, m_id);
}
void fbo::blit(const fbo& dst, areai src_area, areai dst_area, buffers buffers_, filter filter_) const
{
bind_as(target::read_frame_buffer);
dst.bind_as(target::draw_frame_buffer);
glBlitFramebuffer(
src_area.x1, src_area.y1, src_area.x2, src_area.y2,
dst_area.x1, dst_area.y1, dst_area.x2, dst_area.y2,
static_cast<GLbitfield>(buffers_), static_cast<GLenum>(filter_));
}
void fbo::bind_as(target target_) const
{
glBindFramebuffer(static_cast<int>(target_), id());
}
void fbo::remove()
{
if (m_id != GL_NONE)
{
glDeleteFramebuffers(1, &m_id);
m_id = GL_NONE;
}
}
bool fbo::created() const
{
return m_id != GL_NONE;
}
bool fbo::check() const
{
GLenum status = DSA_CALL2_RET(CheckNamedFramebufferStatus, m_id, GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
rsx_log.error("FBO check failed: 0x%04x", status);
return false;
}
return true;
}
void fbo::recreate()
{
if (created())
remove();
create();
}
void fbo::draw_buffer(const attachment& buffer) const
{
GLenum buf = buffer.id();
DSA_CALL3(NamedFramebufferDrawBuffers, FramebufferDrawBuffers, m_id, 1, &buf);
}
void fbo::draw_buffers(const std::initializer_list<attachment>& indexes) const
{
rsx::simple_array<GLenum> ids;
ids.reserve(::size32(indexes));
for (auto& index : indexes)
ids.push_back(index.id());
DSA_CALL3(NamedFramebufferDrawBuffers, FramebufferDrawBuffers, m_id, static_cast<GLsizei>(ids.size()), ids.data());
}
void fbo::read_buffer(const attachment& buffer) const
{
DSA_CALL3(NamedFramebufferReadBuffer, FramebufferReadBuffer, m_id, buffer.id());
}
void fbo::draw_arrays(GLenum mode, GLsizei count, GLint first) const
{
save_binding_state save(*this);
glDrawArrays(mode, first, count);
}
void fbo::draw_arrays(const buffer& buffer, GLenum mode, GLsizei count, GLint first) const
{
buffer.bind(buffer::target::array);
draw_arrays(mode, count, first);
}
void fbo::draw_arrays(const vao& buffer, GLenum mode, GLsizei count, GLint first) const
{
buffer.bind();
draw_arrays(mode, count, first);
}
void fbo::draw_elements(GLenum mode, GLsizei count, indices_type type, const GLvoid* indices) const
{
save_binding_state save(*this);
glDrawElements(mode, count, static_cast<GLenum>(type), indices);
}
void fbo::draw_elements(const buffer& buffer, GLenum mode, GLsizei count, indices_type type, const GLvoid* indices) const
{
buffer.bind(buffer::target::array);
glDrawElements(mode, count, static_cast<GLenum>(type), indices);
}
void fbo::draw_elements(GLenum mode, GLsizei count, indices_type type, const buffer& indices, usz indices_buffer_offset) const
{
indices.bind(buffer::target::element_array);
glDrawElements(mode, count, static_cast<GLenum>(type), reinterpret_cast<GLvoid*>(indices_buffer_offset));
}
void fbo::draw_elements(const buffer& buffer_, GLenum mode, GLsizei count, indices_type type, const buffer& indices, usz indices_buffer_offset) const
{
buffer_.bind(buffer::target::array);
draw_elements(mode, count, type, indices, indices_buffer_offset);
}
void fbo::draw_elements(GLenum mode, GLsizei count, const GLubyte* indices) const
{
draw_elements(mode, count, indices_type::ubyte, indices);
}
void fbo::draw_elements(const buffer& buffer, GLenum mode, GLsizei count, const GLubyte* indices) const
{
draw_elements(buffer, mode, count, indices_type::ubyte, indices);
}
void fbo::draw_elements(GLenum mode, GLsizei count, const GLushort* indices) const
{
draw_elements(mode, count, indices_type::ushort, indices);
}
void fbo::draw_elements(const buffer& buffer, GLenum mode, GLsizei count, const GLushort* indices) const
{
draw_elements(buffer, mode, count, indices_type::ushort, indices);
}
void fbo::draw_elements(GLenum mode, GLsizei count, const GLuint* indices) const
{
draw_elements(mode, count, indices_type::uint, indices);
}
void fbo::draw_elements(const buffer& buffer, GLenum mode, GLsizei count, const GLuint* indices) const
{
draw_elements(buffer, mode, count, indices_type::uint, indices);
}
void fbo::clear(buffers buffers_) const
{
save_binding_state save(*this);
glClear(static_cast<GLbitfield>(buffers_));
}
void fbo::clear(buffers buffers_, color4f color_value, double depth_value, u8 stencil_value) const
{
save_binding_state save(*this);
glClearColor(color_value.r, color_value.g, color_value.b, color_value.a);
glClearDepth(depth_value);
glClearStencil(stencil_value);
clear(buffers_);
}
void fbo::copy_from(const void* pixels, const sizei& size, gl::texture::format format_, gl::texture::type type_, class pixel_unpack_settings pixel_settings) const
{
save_binding_state save(*this);
pixel_settings.apply();
glDrawPixels(size.width, size.height, static_cast<GLenum>(format_), static_cast<GLenum>(type_), pixels);
}
void fbo::copy_from(const buffer& buf, const sizei& size, gl::texture::format format_, gl::texture::type type_, class pixel_unpack_settings pixel_settings) const
{
save_binding_state save(*this);
buffer::save_binding_state save_buffer(buffer::target::pixel_unpack, buf);
pixel_settings.apply();
glDrawPixels(size.width, size.height, static_cast<GLenum>(format_), static_cast<GLenum>(type_), nullptr);
}
void fbo::copy_to(void* pixels, coordi coord, gl::texture::format format_, gl::texture::type type_, class pixel_pack_settings pixel_settings) const
{
save_binding_state save(*this);
pixel_settings.apply();
glReadPixels(coord.x, coord.y, coord.width, coord.height, static_cast<GLenum>(format_), static_cast<GLenum>(type_), pixels);
}
void fbo::copy_to(const buffer& buf, coordi coord, gl::texture::format format_, gl::texture::type type_, class pixel_pack_settings pixel_settings) const
{
save_binding_state save(*this);
buffer::save_binding_state save_buffer(buffer::target::pixel_pack, buf);
pixel_settings.apply();
glReadPixels(coord.x, coord.y, coord.width, coord.height, static_cast<GLenum>(format_), static_cast<GLenum>(type_), nullptr);
}
fbo fbo::get_bound_draw_buffer()
{
GLint value;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &value);
return{ static_cast<GLuint>(value) };
}
fbo fbo::get_bound_read_buffer()
{
GLint value;
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &value);
return{ static_cast<GLuint>(value) };
}
fbo fbo::get_bound_buffer()
{
GLint value;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &value);
return{ static_cast<GLuint>(value) };
}
GLuint fbo::id() const
{
return m_id;
}
void fbo::set_id(GLuint id)
{
m_id = id;
}
void fbo::set_extents(const size2i& extents)
{
m_size = extents;
}
size2i fbo::get_extents() const
{
return m_size;
}
bool fbo::matches(const std::array<GLuint, 4>& color_targets, GLuint depth_stencil_target) const
{
for (u32 index = 0; index < 4; ++index)
{
if (color[index].resource_id() != color_targets[index])
{
return false;
}
}
const auto depth_resource = depth.resource_id() | depth_stencil.resource_id();
return (depth_resource == depth_stencil_target);
}
bool fbo::references_any(const std::vector<GLuint>& resources) const
{
return std::any_of(m_resource_bindings.cbegin(), m_resource_bindings.cend(), [&resources](const auto& e)
{
return std::find(resources.cbegin(), resources.cend(), e.second) != resources.cend();
});
}
}

View File

@ -0,0 +1,242 @@
#pragma once
#include "common.h"
#include "image.h"
#include "pixel_settings.hpp"
#include "Utilities/geometry.h"
namespace gl
{
enum class buffers
{
none = 0,
color = GL_COLOR_BUFFER_BIT,
depth = GL_DEPTH_BUFFER_BIT,
stencil = GL_STENCIL_BUFFER_BIT,
color_depth = color | depth,
color_depth_stencil = color | depth | stencil,
color_stencil = color | stencil,
depth_stencil = depth | stencil
};
enum class indices_type
{
ubyte = GL_UNSIGNED_BYTE,
ushort = GL_UNSIGNED_SHORT,
uint = GL_UNSIGNED_INT
};
class vao;
class fbo
{
GLuint m_id = GL_NONE;
size2i m_size;
protected:
std::unordered_map<GLenum, GLuint> m_resource_bindings;
public:
fbo() = default;
fbo(GLuint id)
{
set_id(id);
}
~fbo()
{
if (created())
remove();
}
class save_binding_state
{
GLint m_last_binding;
bool reset = true;
public:
save_binding_state(const fbo& new_binding)
{
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_last_binding);
if (m_last_binding + 0u != new_binding.id())
new_binding.bind();
else
reset = false;
}
~save_binding_state()
{
if (reset)
glBindFramebuffer(GL_FRAMEBUFFER, m_last_binding);
}
};
class attachment
{
public:
enum class type
{
color = GL_COLOR_ATTACHMENT0,
depth = GL_DEPTH_ATTACHMENT,
stencil = GL_STENCIL_ATTACHMENT,
depth_stencil = GL_DEPTH_STENCIL_ATTACHMENT
};
protected:
GLuint m_id = GL_NONE;
fbo& m_parent;
attachment(fbo& parent)
: m_parent(parent)
{}
public:
attachment(fbo& parent, type type)
: m_id(static_cast<int>(type))
, m_parent(parent)
{
}
void set_id(uint id)
{
m_id = id;
}
uint id() const
{
return m_id;
}
GLuint resource_id() const
{
const auto found = m_parent.m_resource_bindings.find(m_id);
if (found != m_parent.m_resource_bindings.end())
{
return found->second;
}
return 0;
}
void operator = (const texture& rhs)
{
ensure(rhs.get_target() == texture::target::texture2D);
m_parent.m_resource_bindings[m_id] = rhs.id();
DSA_CALL2(NamedFramebufferTexture, m_parent.id(), m_id, rhs.id(), 0);
}
void operator = (const GLuint rhs)
{
m_parent.m_resource_bindings[m_id] = rhs;
DSA_CALL2(NamedFramebufferTexture, m_parent.id(), m_id, rhs, 0);
}
};
class indexed_attachment : public attachment
{
public:
indexed_attachment(fbo& parent, type type) : attachment(parent, type)
{
}
attachment operator[](int index) const
{
return{ m_parent, type(id() + index) };
}
std::vector<attachment> range(int from, int count) const
{
std::vector<attachment> result;
for (int i = from; i < from + count; ++i)
result.push_back((*this)[i]);
return result;
}
using attachment::operator =;
};
struct null_attachment : public attachment
{
null_attachment(fbo& parent)
: attachment(parent)
{}
};
indexed_attachment color{ *this, attachment::type::color };
attachment depth{ *this, attachment::type::depth };
attachment stencil{ *this, attachment::type::stencil };
attachment depth_stencil{ *this, attachment::type::depth_stencil };
null_attachment no_color{ *this };
enum class target
{
read_frame_buffer = GL_READ_FRAMEBUFFER,
draw_frame_buffer = GL_DRAW_FRAMEBUFFER
};
void create();
void bind() const;
void blit(const fbo& dst, areai src_area, areai dst_area, buffers buffers_ = buffers::color, filter filter_ = filter::nearest) const;
void bind_as(target target_) const;
void remove();
bool created() const;
bool check() const;
void recreate();
void draw_buffer(const attachment& buffer) const;
void draw_buffers(const std::initializer_list<attachment>& indexes) const;
void read_buffer(const attachment& buffer) const;
void draw_arrays(GLenum mode, GLsizei count, GLint first = 0) const;
void draw_arrays(const buffer& buffer, GLenum mode, GLsizei count, GLint first = 0) const;
void draw_arrays(const vao& buffer, GLenum mode, GLsizei count, GLint first = 0) const;
void draw_elements(GLenum mode, GLsizei count, indices_type type, const GLvoid* indices) const;
void draw_elements(const buffer& buffer, GLenum mode, GLsizei count, indices_type type, const GLvoid* indices) const;
void draw_elements(GLenum mode, GLsizei count, indices_type type, const buffer& indices, usz indices_buffer_offset = 0) const;
void draw_elements(const buffer& buffer_, GLenum mode, GLsizei count, indices_type type, const buffer& indices, usz indices_buffer_offset = 0) const;
void draw_elements(GLenum mode, GLsizei count, const GLubyte* indices) const;
void draw_elements(const buffer& buffer, GLenum mode, GLsizei count, const GLubyte* indices) const;
void draw_elements(GLenum mode, GLsizei count, const GLushort* indices) const;
void draw_elements(const buffer& buffer, GLenum mode, GLsizei count, const GLushort* indices) const;
void draw_elements(GLenum mode, GLsizei count, const GLuint* indices) const;
void draw_elements(const buffer& buffer, GLenum mode, GLsizei count, const GLuint* indices) const;
void clear(buffers buffers_) const;
void clear(buffers buffers_, color4f color_value, double depth_value, u8 stencil_value) const;
void copy_from(const void* pixels, const sizei& size, gl::texture::format format_, gl::texture::type type_, class pixel_unpack_settings pixel_settings = pixel_unpack_settings()) const;
void copy_from(const buffer& buf, const sizei& size, gl::texture::format format_, gl::texture::type type_, class pixel_unpack_settings pixel_settings = pixel_unpack_settings()) const;
void copy_to(void* pixels, coordi coord, gl::texture::format format_, gl::texture::type type_, class pixel_pack_settings pixel_settings = pixel_pack_settings()) const;
void copy_to(const buffer& buf, coordi coord, gl::texture::format format_, gl::texture::type type_, class pixel_pack_settings pixel_settings = pixel_pack_settings()) const;
static fbo get_bound_draw_buffer();
static fbo get_bound_read_buffer();
static fbo get_bound_buffer();
GLuint id() const;
void set_id(GLuint id);
void set_extents(const size2i& extents);
size2i get_extents() const;
bool matches(const std::array<GLuint, 4>& color_targets, GLuint depth_stencil_target) const;
bool references_any(const std::vector<GLuint>& resources) const;
explicit operator bool() const
{
return created();
}
};
extern const fbo screen;
}

View File

@ -224,7 +224,7 @@ namespace gl
}
}
void texture_view::create(texture* data, GLenum target, GLenum sized_format, GLenum aspect_flags, const GLenum* argb_swizzle)
void texture_view::create(texture* data, GLenum target, GLuint min_level, GLuint num_levels, GLenum sized_format, GLenum aspect_flags, const GLenum* argb_swizzle)
{
m_target = target;
m_format = sizedfmt_to_ifmt(sized_format);
@ -243,7 +243,7 @@ namespace gl
}
glGenTextures(1, &m_id);
glTextureView(m_id, target, data->id(), m_format, 0, data->levels(), 0, num_layers);
glTextureView(m_id, target, data->id(), m_format, min_level, num_levels, 0, num_layers);
if (argb_swizzle)
{

View File

@ -25,6 +25,22 @@ namespace gl
stencil = 4
};
enum class filter
{
nearest = GL_NEAREST,
linear = GL_LINEAR
};
enum class min_filter
{
nearest = GL_NEAREST,
linear = GL_LINEAR,
nearest_mipmap_nearest = GL_NEAREST_MIPMAP_NEAREST,
nearest_mipmap_linear = GL_NEAREST_MIPMAP_LINEAR,
linear_mipmap_nearest = GL_LINEAR_MIPMAP_NEAREST,
linear_mipmap_linear = GL_LINEAR_MIPMAP_LINEAR
};
class texture
{
friend class texture_view;
@ -301,7 +317,7 @@ namespace gl
GLenum component_swizzle[4];
void create(texture* data, GLenum target, GLenum sized_format, GLenum aspect_flags, const GLenum* argb_swizzle = nullptr);
void create(texture* data, GLenum target, GLenum sized_format, GLuint min_level, GLuint num_levels, GLenum aspect_flags, const GLenum* argb_swizzle = nullptr);
public:
texture_view(const texture_view&) = delete;
@ -311,7 +327,7 @@ namespace gl
const GLenum* argb_swizzle = nullptr,
GLenum aspect_flags = image_aspect::color | image_aspect::depth)
{
create(data, target, sized_format, aspect_flags, argb_swizzle);
create(data, target, sized_format, 0, data->levels(), aspect_flags, argb_swizzle);
}
texture_view(texture* data, const GLenum* argb_swizzle = nullptr,
@ -319,7 +335,16 @@ namespace gl
{
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);
create(data, target, sized_format, 0, data->levels(), aspect_flags, argb_swizzle);
}
texture_view(texture* data, GLuint mip_level,
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, mip_level, 1, aspect_flags, argb_swizzle);
}
virtual ~texture_view();

View File

@ -0,0 +1,217 @@
#include "stdafx.h"
#include "program.h"
#include "state_tracker.hpp"
#include "Emu/system_config.h"
namespace gl
{
namespace glsl
{
void shader::precompile()
{
const char* str = source.c_str();
const GLint length = ::narrow<GLint>(source.length());
if (g_cfg.video.log_programs)
{
std::string base_name;
switch (type)
{
case ::glsl::program_domain::glsl_vertex_program:
base_name = "shaderlog/VertexProgram";
break;
case ::glsl::program_domain::glsl_fragment_program:
base_name = "shaderlog/FragmentProgram";
break;
case ::glsl::program_domain::glsl_compute_program:
base_name = "shaderlog/ComputeProgram";
break;
}
fs::file(fs::get_cache_dir() + base_name + std::to_string(m_id) + ".glsl", fs::rewrite).write(str, length);
}
glShaderSource(m_id, 1, &str, &length);
m_init_fence.create();
flush_command_queue(m_init_fence);
}
void shader::create(::glsl::program_domain type_, const std::string & src)
{
type = type_;
source = src;
GLenum shader_type{};
switch (type)
{
case ::glsl::program_domain::glsl_vertex_program:
shader_type = GL_VERTEX_SHADER;
break;
case ::glsl::program_domain::glsl_fragment_program:
shader_type = GL_FRAGMENT_SHADER;
break;
case ::glsl::program_domain::glsl_compute_program:
shader_type = GL_COMPUTE_SHADER;
break;
default:
rsx_log.fatal("gl::glsl::shader::compile(): Unhandled shader type (%d)", +type_);
return;
}
m_id = glCreateShader(shader_type);
precompile();
}
shader& shader::compile()
{
std::lock_guard lock(m_compile_lock);
if (m_is_compiled)
{
// Another thread compiled this already
return *this;
}
ensure(!m_init_fence.is_empty()); // Do not attempt to compile a shader_view!!
m_init_fence.server_wait_sync();
glCompileShader(m_id);
GLint status = GL_FALSE;
glGetShaderiv(m_id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
GLint length = 0;
glGetShaderiv(m_id, GL_INFO_LOG_LENGTH, &length);
std::string error_msg;
if (length)
{
std::unique_ptr<GLchar[]> buf(new char[length + 1]);
glGetShaderInfoLog(m_id, length, nullptr, buf.get());
error_msg = buf.get();
}
rsx_log.fatal("Compilation failed: %s\nsource: %s", error_msg, source);
}
m_compiled_fence.create();
flush_command_queue(m_compiled_fence);
m_is_compiled = true;
return *this;
}
bool program::uniforms_t::has_location(const std::string & name, int* location)
{
auto found = locations.find(name);
if (found != locations.end())
{
if (location)
{
*location = found->second;
}
return (found->second >= 0);
}
auto result = glGetUniformLocation(m_program.id(), name.c_str());
locations[name] = result;
if (location)
{
*location = result;
}
return (result >= 0);
}
GLint program::uniforms_t::location(const std::string& name)
{
auto found = locations.find(name);
if (found != locations.end())
{
if (found->second >= 0)
{
return found->second;
}
else
{
rsx_log.fatal("%s not found.", name);
return -1;
}
}
auto result = glGetUniformLocation(m_program.id(), name.c_str());
if (result < 0)
{
rsx_log.fatal("%s not found.", name);
return result;
}
locations[name] = result;
return result;
}
void program::link(std::function<void(program*)> init_func)
{
glLinkProgram(m_id);
GLint status = GL_FALSE;
glGetProgramiv(m_id, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
GLint length = 0;
glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, &length);
std::string error_msg;
if (length)
{
std::unique_ptr<GLchar[]> buf(new char[length + 1]);
glGetProgramInfoLog(m_id, length, nullptr, buf.get());
error_msg = buf.get();
}
rsx_log.fatal("Linkage failed: %s", error_msg);
}
else
{
if (init_func)
{
init_func(this);
}
m_fence.create();
flush_command_queue(m_fence);
}
}
void program::validate()
{
glValidateProgram(m_id);
GLint status = GL_FALSE;
glGetProgramiv(m_id, GL_VALIDATE_STATUS, &status);
if (status == GL_FALSE)
{
GLint length = 0;
glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, &length);
std::string error_msg;
if (length)
{
std::unique_ptr<GLchar[]> buf(new char[length + 1]);
glGetProgramInfoLog(m_id, length, nullptr, buf.get());
error_msg = buf.get();
}
rsx_log.error("Validation failed: %s", error_msg.c_str());
}
}
}
}

View File

@ -0,0 +1,197 @@
#pragma once
#include "common.h"
#include "sync.hpp"
#include "Emu/RSX/Program/GLSLTypes.h"
#include "Utilities/geometry.h"
#include "Utilities/mutex.h"
namespace gl
{
namespace glsl
{
class shader
{
std::string source;
::glsl::program_domain type;
GLuint m_id = GL_NONE;
fence m_compiled_fence;
fence m_init_fence;
shared_mutex m_compile_lock;
atomic_t<bool> m_is_compiled{};
void precompile();
public:
shader() = default;
shader(::glsl::program_domain type_, const std::string& src)
{
create(type_, src);
}
~shader()
{
if (created())
{
remove();
}
}
void remove()
{
glDeleteShader(m_id);
m_id = GL_NONE;
}
void create(::glsl::program_domain type_, const std::string& src);
shader& compile();
uint id() const { return m_id; }
const std::string& get_source() const { return source; }
fence get_compile_fence_sync() const { return m_compiled_fence; }
bool created() const { return m_id != GL_NONE; }
bool compiled() const { return m_is_compiled; }
explicit operator bool() const { return created(); }
};
class program
{
GLuint m_id = GL_NONE;
fence m_fence;
public:
class uniform_t
{
program& m_program;
GLint m_location;
public:
uniform_t(program& program, GLint location)
: m_program(program)
, m_location(location)
{
}
GLint location() const
{
return m_location;
}
void operator = (int rhs) const { glProgramUniform1i(m_program.id(), location(), rhs); }
void operator = (unsigned rhs) const { glProgramUniform1ui(m_program.id(), location(), rhs); }
void operator = (float rhs) const { glProgramUniform1f(m_program.id(), location(), rhs); }
void operator = (bool rhs) const { glProgramUniform1ui(m_program.id(), location(), rhs ? 1 : 0); }
void operator = (const color1i& rhs) const { glProgramUniform1i(m_program.id(), location(), rhs.r); }
void operator = (const color1f& rhs) const { glProgramUniform1f(m_program.id(), location(), rhs.r); }
void operator = (const color2i& rhs) const { glProgramUniform2i(m_program.id(), location(), rhs.r, rhs.g); }
void operator = (const color2f& rhs) const { glProgramUniform2f(m_program.id(), location(), rhs.r, rhs.g); }
void operator = (const color3i& rhs) const { glProgramUniform3i(m_program.id(), location(), rhs.r, rhs.g, rhs.b); }
void operator = (const color3f& rhs) const { glProgramUniform3f(m_program.id(), location(), rhs.r, rhs.g, rhs.b); }
void operator = (const color4i& rhs) const { glProgramUniform4i(m_program.id(), location(), rhs.r, rhs.g, rhs.b, rhs.a); }
void operator = (const color4f& rhs) const { glProgramUniform4f(m_program.id(), location(), rhs.r, rhs.g, rhs.b, rhs.a); }
void operator = (const areaf& rhs) const { glProgramUniform4f(m_program.id(), location(), rhs.x1, rhs.y1, rhs.x2, rhs.y2); }
void operator = (const areai& rhs) const { glProgramUniform4i(m_program.id(), location(), rhs.x1, rhs.y1, rhs.x2, rhs.y2); }
void operator = (const std::vector<int>& rhs) const { glProgramUniform1iv(m_program.id(), location(), ::size32(rhs), rhs.data()); }
};
class uniforms_t
{
program& m_program;
std::unordered_map<std::string, GLint> locations;
public:
uniforms_t(program* program)
: m_program(*program)
{}
void clear() { locations.clear(); }
bool has_location(const std::string& name, int* location = nullptr);
GLint location(const std::string& name);
uniform_t operator[](GLint location) { return{ m_program, location }; }
uniform_t operator[](const std::string& name) { return{ m_program, location(name) }; }
} uniforms{ this };
public:
program() = default;
program(const program&) = delete;
~program()
{
if (created())
{
remove();
}
}
program& recreate()
{
remove();
return create();
}
program& create()
{
m_id = glCreateProgram();
return *this;
}
void remove()
{
glDeleteProgram(m_id);
m_id = GL_NONE;
uniforms.clear();
}
void link(std::function<void(program*)> init_func = {});
void validate();
void sync()
{
if (!m_fence.check_signaled())
{
m_fence.server_wait_sync();
}
}
program& attach(const shader& shader_)
{
glAttachShader(m_id, shader_.id());
return *this;
}
program& bind_attribute_location(const std::string& name, int index)
{
glBindAttribLocation(m_id, index, name.c_str());
return *this;
}
program& bind_fragment_data_location(const std::string& name, int color_number)
{
glBindFragDataLocation(m_id, color_number, name.c_str());
return *this;
}
GLuint id() const { return m_id; }
bool created() const { return m_id != GL_NONE; }
explicit operator bool() const { return created(); }
};
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "buffer_object.h"
#include "sync.hpp"
#include "Utilities/address_range.h"
namespace gl

View File

@ -348,4 +348,7 @@ namespace gl
void set_primary_context_thread(bool = true);
bool is_primary_context_thread();
class fence;
void flush_command_queue(fence& fence_obj);
}

View File

@ -0,0 +1,129 @@
#pragma once
#include "common.h"
namespace gl
{
class fence
{
GLsync m_value = nullptr;
mutable GLenum flags = GL_SYNC_FLUSH_COMMANDS_BIT;
mutable bool signaled = false;
public:
fence() = default;
~fence() = default;
void create()
{
m_value = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
flags = GL_SYNC_FLUSH_COMMANDS_BIT;
}
void destroy()
{
glDeleteSync(m_value);
m_value = nullptr;
}
void reset()
{
if (m_value != nullptr)
destroy();
create();
}
bool is_empty() const
{
return (m_value == nullptr);
}
bool check_signaled() const
{
ensure(m_value);
if (signaled)
return true;
if (flags)
{
GLenum err = glClientWaitSync(m_value, flags, 0);
flags = 0;
if (!(err == GL_ALREADY_SIGNALED || err == GL_CONDITION_SATISFIED))
return false;
}
else
{
GLint status = GL_UNSIGNALED;
GLint tmp;
glGetSynciv(m_value, GL_SYNC_STATUS, 4, &tmp, &status);
if (status != GL_SIGNALED)
return false;
}
signaled = true;
return true;
}
bool wait_for_signal()
{
ensure(m_value);
if (signaled == GL_FALSE)
{
GLenum err = GL_WAIT_FAILED;
bool done = false;
while (!done)
{
if (flags)
{
err = glClientWaitSync(m_value, flags, 0);
flags = 0;
switch (err)
{
default:
rsx_log.error("gl::fence sync returned unknown error 0x%X", err);
[[fallthrough]];
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:
done = true;
break;
case GL_TIMEOUT_EXPIRED:
continue;
}
}
else
{
GLint status = GL_UNSIGNALED;
GLint tmp;
glGetSynciv(m_value, GL_SYNC_STATUS, 4, &tmp, &status);
if (status == GL_SIGNALED)
break;
}
}
signaled = (err == GL_ALREADY_SIGNALED || err == GL_CONDITION_SATISFIED);
}
glDeleteSync(m_value);
m_value = nullptr;
return signaled;
}
void server_wait_sync() const
{
ensure(m_value != nullptr);
glWaitSync(m_value, 0, GL_TIMEOUT_IGNORED);
}
};
}

View File

@ -0,0 +1,306 @@
#pragma once
#include "common.h"
#include "buffer_object.h"
#include "Utilities/geometry.h"
namespace gl
{
class vao;
class buffer_pointer
{
public:
enum class type
{
s8 = GL_BYTE,
u8 = GL_UNSIGNED_BYTE,
s16 = GL_SHORT,
u16 = GL_UNSIGNED_SHORT,
s32 = GL_INT,
u32 = GL_UNSIGNED_INT,
f16 = GL_HALF_FLOAT,
f32 = GL_FLOAT,
f64 = GL_DOUBLE,
fixed = GL_FIXED,
s32_2_10_10_10_rev = GL_INT_2_10_10_10_REV,
u32_2_10_10_10_rev = GL_UNSIGNED_INT_2_10_10_10_REV,
u32_10f_11f_11f_rev = GL_UNSIGNED_INT_10F_11F_11F_REV
};
private:
vao* m_vao;
u32 m_offset;
u32 m_stride;
u32 m_size = 4;
type m_type = type::f32;
bool m_normalize = false;
public:
buffer_pointer(vao* vao, u32 offset = 0, u32 stride = 0)
: m_vao(vao)
, m_offset(offset)
, m_stride(stride)
{
}
const class ::gl::vao& get_vao() const
{
return *m_vao;
}
class ::gl::vao& get_vao()
{
return *m_vao;
}
buffer_pointer& offset(u32 value)
{
m_offset = value;
return *this;
}
u32 offset() const
{
return m_offset;
}
buffer_pointer& stride(u32 value)
{
m_stride = value;
return *this;
}
u32 stride() const
{
return m_stride;
}
buffer_pointer& size(u32 value)
{
m_size = value;
return *this;
}
u32 size() const
{
return m_size;
}
buffer_pointer& set_type(type value)
{
m_type = value;
return *this;
}
type get_type() const
{
return m_type;
}
buffer_pointer& normalize(bool value)
{
m_normalize = value;
return *this;
}
bool normalize() const
{
return m_normalize;
}
buffer_pointer& operator >> (u32 value)
{
return stride(value);
}
buffer_pointer& config(type type_ = type::f32, u32 size_ = 4, bool normalize_ = false)
{
return set_type(type_).size(size_).normalize(normalize_);
}
};
class attrib_t;
class vao
{
template<buffer::target BindId, uint GetStateId>
class entry
{
vao& m_parent;
public:
using save_binding_state = save_binding_state_base<entry, (static_cast<GLuint>(BindId)), GetStateId>;
entry(vao* parent) noexcept : m_parent(*parent)
{
}
entry& operator = (const buffer& buf) noexcept
{
m_parent.bind();
buf.bind(BindId);
return *this;
}
};
GLuint m_id = GL_NONE;
public:
entry<buffer::target::pixel_pack, GL_PIXEL_PACK_BUFFER_BINDING> pixel_pack_buffer{ this };
entry<buffer::target::pixel_unpack, GL_PIXEL_UNPACK_BUFFER_BINDING> pixel_unpack_buffer{ this };
entry<buffer::target::array, GL_ARRAY_BUFFER_BINDING> array_buffer{ this };
entry<buffer::target::element_array, GL_ELEMENT_ARRAY_BUFFER_BINDING> element_array_buffer{ this };
vao() = default;
vao(const vao&) = delete;
vao(vao&& vao_) noexcept
{
swap(vao_);
}
vao(GLuint id) noexcept
{
set_id(id);
}
~vao() noexcept
{
if (created())
remove();
}
void swap(vao& vao_) noexcept
{
auto my_old_id = id();
set_id(vao_.id());
vao_.set_id(my_old_id);
}
vao& operator = (const vao& rhs) = delete;
vao& operator = (vao&& rhs) noexcept
{
swap(rhs);
return *this;
}
void bind() const noexcept
{
glBindVertexArray(m_id);
}
void create() noexcept
{
glGenVertexArrays(1, &m_id);
}
void remove() noexcept
{
if (m_id != GL_NONE)
{
glDeleteVertexArrays(1, &m_id);
m_id = GL_NONE;
}
}
uint id() const noexcept
{
return m_id;
}
void set_id(uint id) noexcept
{
m_id = id;
}
bool created() const noexcept
{
return m_id != GL_NONE;
}
explicit operator bool() const noexcept
{
return created();
}
void enable_for_attributes(std::initializer_list<GLuint> indexes) noexcept
{
for (auto& index : indexes)
{
glEnableVertexAttribArray(index);
}
}
void disable_for_attributes(std::initializer_list<GLuint> indexes) noexcept
{
for (auto& index : indexes)
{
glDisableVertexAttribArray(index);
}
}
void enable_for_attribute(GLuint index) noexcept
{
enable_for_attributes({ index });
}
void disable_for_attribute(GLuint index) noexcept
{
disable_for_attributes({ index });
}
buffer_pointer operator + (u32 offset) noexcept
{
return{ this, offset };
}
buffer_pointer operator >> (u32 stride) noexcept
{
return{ this, {}, stride };
}
operator buffer_pointer() noexcept
{
return{ this };
}
attrib_t operator [] (u32 index) const noexcept;
};
class attrib_t
{
GLint m_location;
public:
attrib_t(GLint location)
: m_location(location)
{
}
GLint location() const
{
return m_location;
}
void operator = (float rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1f(location(), rhs); }
void operator = (double rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1d(location(), rhs); }
void operator = (const color1f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1f(location(), rhs.r); }
void operator = (const color1d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1d(location(), rhs.r); }
void operator = (const color2f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib2f(location(), rhs.r, rhs.g); }
void operator = (const color2d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib2d(location(), rhs.r, rhs.g); }
void operator = (const color3f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib3f(location(), rhs.r, rhs.g, rhs.b); }
void operator = (const color3d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib3d(location(), rhs.r, rhs.g, rhs.b); }
void operator = (const color4f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib4f(location(), rhs.r, rhs.g, rhs.b, rhs.a); }
void operator = (const color4d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib4d(location(), rhs.r, rhs.g, rhs.b, rhs.a); }
void operator = (buffer_pointer& pointer) const
{
pointer.get_vao().enable_for_attribute(m_location);
glVertexAttribPointer(location(), pointer.size(), static_cast<GLenum>(pointer.get_type()), pointer.normalize(),
pointer.stride(), reinterpret_cast<const void*>(u64{ pointer.offset() }));
}
};
}

View File

@ -60,13 +60,18 @@
<ClInclude Include="Emu\RSX\GL\GLGSRender.h" />
<ClInclude Include="Emu\RSX\GL\GLProcTable.h" />
<ClInclude Include="Emu\RSX\GL\GLProgramBuffer.h" />
<ClInclude Include="Emu\RSX\GL\glutils\blitter.h" />
<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\fbo.h" />
<ClInclude Include="Emu\RSX\GL\glutils\pixel_settings.hpp" />
<ClInclude Include="Emu\RSX\GL\glutils\program.h" />
<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\glutils\sync.hpp" />
<ClInclude Include="Emu\RSX\GL\glutils\vao.hpp" />
<ClInclude Include="Emu\RSX\GL\GLVertexProgram.h" />
<ClInclude Include="Emu\RSX\GL\GLHelpers.h" />
<ClInclude Include="Emu\RSX\GL\GLRenderTargets.h" />
@ -83,8 +88,11 @@
<ClCompile Include="Emu\RSX\GL\GLGSRender.cpp" />
<ClCompile Include="Emu\RSX\GL\GLOverlays.cpp" />
<ClCompile Include="Emu\RSX\GL\GLPipelineCompiler.cpp" />
<ClCompile Include="Emu\RSX\GL\glutils\blitter.cpp" />
<ClCompile Include="Emu\RSX\GL\glutils\buffer_object.cpp" />
<ClCompile Include="Emu\RSX\GL\glutils\common.cpp" />
<ClCompile Include="Emu\RSX\GL\glutils\fbo.cpp" />
<ClCompile Include="Emu\RSX\GL\glutils\program.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" />

View File

@ -29,6 +29,15 @@
<ClCompile Include="Emu\RSX\GL\glutils\common.cpp">
<Filter>glutils</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\GL\glutils\blitter.cpp">
<Filter>glutils</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\GL\glutils\fbo.cpp">
<Filter>glutils</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\GL\glutils\program.cpp">
<Filter>glutils</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\RSX\GL\GLTexture.h" />
@ -68,6 +77,21 @@
<ClInclude Include="Emu\RSX\GL\glutils\pixel_settings.hpp">
<Filter>glutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\GL\glutils\blitter.h">
<Filter>glutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\GL\glutils\fbo.h">
<Filter>glutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\GL\glutils\vao.hpp">
<Filter>glutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\GL\glutils\program.h">
<Filter>glutils</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\GL\glutils\sync.hpp">
<Filter>glutils</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="glutils">