gl: Clean up the awful image copy API

- Wrap everything in rsx::io_buffer and gl::buffer to avoid out of bounds issues.
- Also abstracts away nasty things like OpenGL offset pointer casts.
This commit is contained in:
kd-11 2026-03-07 00:23:58 +03:00 committed by kd-11
parent ac30feeddb
commit 14789b536f
9 changed files with 62 additions and 48 deletions

View file

@ -9,7 +9,7 @@ namespace rsx
template <typename T>
concept SpanLike = requires(T t)
{
{ t.data() } -> std::convertible_to<void*>;
{ t.data() } -> std::convertible_to<const void*>;
{ t.size_bytes() } -> std::convertible_to<usz>;
};
@ -71,9 +71,10 @@ namespace rsx
return static_cast<T*>(m_ptr);
}
usz size() const
template <Integral T = usz>
T size() const
{
return m_size;
return static_cast<T>(m_size);
}
template<typename T>

View file

@ -249,22 +249,23 @@ void GLGSRender::on_init_thread()
// Fallback null texture instead of relying on texture0
{
std::array<u32, 8> pixeldata = { 0, 0, 0, 0, 0, 0, 0, 0 };
const rsx::io_buffer src_buf = std::span<u32>(pixeldata);
// 1D
auto tex1D = std::make_unique<gl::texture>(GL_TEXTURE_1D, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR);
tex1D->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
tex1D->copy_from(src_buf, gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
// 2D
auto tex2D = std::make_unique<gl::texture>(GL_TEXTURE_2D, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR);
tex2D->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
tex2D->copy_from(src_buf, gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
// 3D
auto tex3D = std::make_unique<gl::texture>(GL_TEXTURE_3D, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR);
tex3D->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
tex3D->copy_from(src_buf, gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
// CUBE
auto texCUBE = std::make_unique<gl::texture>(GL_TEXTURE_CUBE_MAP, 1, 1, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR);
texCUBE->copy_from(pixeldata.data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
texCUBE->copy_from(src_buf, gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
m_null_textures[GL_TEXTURE_1D] = std::move(tex1D);
m_null_textures[GL_TEXTURE_2D] = std::move(tex2D);

View file

@ -223,7 +223,7 @@ namespace gl
gl::texture_view* ui_overlay_renderer::load_simple_image(rsx::overlays::image_info_base* desc, bool temp_resource, u32 owner_uid)
{
auto tex = std::make_unique<gl::texture>(GL_TEXTURE_2D, desc->w, desc->h, 1, 1, 1, GL_RGBA8, RSX_FORMAT_CLASS_COLOR);
tex->copy_from(desc->get_data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
tex->copy_from(desc->as_span(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
const GLenum remap[] = { GL_RED, GL_ALPHA, GL_BLUE, GL_GREEN };
auto view = std::make_unique<gl::texture_view>(tex.get(), remap);
@ -308,7 +308,7 @@ namespace gl
const std::vector<u8>& glyph_data = font->get_glyph_data();
auto tex = std::make_unique<gl::texture>(GL_TEXTURE_2D_ARRAY, font_size.width, font_size.height, font_size.depth, 1, 1, GL_R8, RSX_FORMAT_CLASS_COLOR);
tex->copy_from(glyph_data.data(), gl::texture::format::r, gl::texture::type::ubyte, {});
tex->copy_from(std::span<const u8>(glyph_data), gl::texture::format::r, gl::texture::type::ubyte, {});
GLenum remap[] = { GL_RED, GL_RED, GL_RED, GL_RED };
auto view = std::make_unique<gl::texture_view>(tex.get(), remap);
@ -332,7 +332,7 @@ namespace gl
if (dirty)
{
view->image()->copy_from(desc->get_data(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
view->image()->copy_from(desc->as_span(), gl::texture::format::rgba, gl::texture::type::uint_8_8_8_8, {});
}
return view;

View file

@ -132,7 +132,8 @@ gl::texture* GLGSRender::get_present_source(gl::present_surface_info* info, cons
const auto range = utils::address_range32::start_length(info->address, info->pitch * info->height);
m_gl_texture_cache.invalidate_range(cmd, range, rsx::invalidation_cause::read);
flip_image->copy_from(vm::base(info->address), static_cast<gl::texture::format>(expected_format), gl::texture::type::uint_8_8_8_8, unpack_settings);
const rsx::io_buffer read_buf = { vm::base(info->address), range.length() };
flip_image->copy_from(read_buf, static_cast<gl::texture::format>(expected_format), gl::texture::type::uint_8_8_8_8, unpack_settings);
image = flip_image.get();
}
else if (image->get_internal_format() != static_cast<gl::texture::internal_format>(expected_format))
@ -368,7 +369,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
std::vector<u8> sshot_frame(buffer_height * buffer_width * 4);
glGetError();
tex->copy_to(sshot_frame.data(), gl::texture::format::rgba, gl::texture::type::ubyte, pack_settings);
tex->copy_to(std::span<const u8>(sshot_frame), gl::texture::format::rgba, gl::texture::type::ubyte, pack_settings);
m_sshot_tex.reset();

View file

@ -363,8 +363,7 @@ namespace gl
}
}
dst->bind(buffer::target::pixel_pack);
src->copy_to(reinterpret_cast<void*>(static_cast<uintptr_t>(dst_offset)), static_cast<texture::format>(pack_info.format), static_cast<texture::type>(pack_info.type), src_level, src_region, {});
src->copy_to(*dst, dst_offset, static_cast<texture::format>(pack_info.format), static_cast<texture::type>(pack_info.type), src_level, src_region, {});
return false;
};
@ -611,9 +610,8 @@ namespace gl
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, GL_NONE);
transfer_buf->bind(buffer::target::pixel_unpack);
dst->copy_from(reinterpret_cast<void*>(u64(out_offset)), static_cast<texture::format>(unpack_info.format),
dst->copy_from(*transfer_buf, out_offset, static_cast<texture::format>(unpack_info.format),
static_cast<texture::type>(unpack_info.type), dst_level, dst_region, {});
}
}
@ -712,7 +710,6 @@ namespace gl
pixel_buffer_layout mem_layout;
std::span<std::byte> dst_buffer = staging_buffer;
void* out_pointer = staging_buffer.data();
u8 block_size_in_bytes = rsx::get_format_block_size_in_bytes(format);
u64 image_linear_size = staging_buffer.size();
@ -731,8 +728,6 @@ namespace gl
g_compute_decode_buffer.remove();
g_compute_decode_buffer.create(gl::buffer::target::ssbo, min_required_buffer_size);
}
out_pointer = nullptr;
}
for (const rsx::subresource_layout& layout : input_layouts)
@ -867,7 +862,7 @@ namespace gl
else
{
unpack_settings.swap_bytes(op.require_swap);
dst->copy_from(out_pointer, static_cast<texture::format>(gl_format), static_cast<texture::type>(gl_type), layout.level, region, unpack_settings);
dst->copy_from(staging_buffer, static_cast<texture::format>(gl_format), static_cast<texture::type>(gl_type), layout.level, region, unpack_settings);
}
}
}
@ -1156,9 +1151,7 @@ namespace gl
// Start pack operation
pixel_pack_settings pack_settings{};
pack_settings.swap_bytes(pack_info.swap_bytes);
g_typeless_transfer_buffer.get().bind(buffer::target::pixel_pack);
src->copy_to(nullptr, static_cast<texture::format>(pack_info.format), static_cast<texture::type>(pack_info.type), 0, src_region, pack_settings);
src->copy_to(g_typeless_transfer_buffer.get(), 0, static_cast<texture::format>(pack_info.format), static_cast<texture::type>(pack_info.type), 0, src_region, pack_settings);
glBindBuffer(GL_PIXEL_PACK_BUFFER, GL_NONE);
@ -1166,8 +1159,7 @@ namespace gl
pixel_unpack_settings unpack_settings{};
unpack_settings.swap_bytes(unpack_info.swap_bytes);
g_typeless_transfer_buffer.get().bind(buffer::target::pixel_unpack);
dst->copy_from(nullptr, static_cast<texture::format>(unpack_info.format), static_cast<texture::type>(unpack_info.type), 0, dst_region, unpack_settings);
dst->copy_from(g_typeless_transfer_buffer.get(), 0, static_cast<texture::format>(unpack_info.format), static_cast<texture::type>(unpack_info.type), 0, dst_region, unpack_settings);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, GL_NONE);
}
}

View file

@ -233,13 +233,11 @@ namespace gl
pack_unpack_swap_bytes = false;
}
pbo.bind(buffer::target::pixel_pack);
pixel_pack_settings pack_settings;
pack_settings.alignment(1);
pack_settings.swap_bytes(pack_unpack_swap_bytes);
src->copy_to(reinterpret_cast<void*>(pbo_offset), format, type, 0, src_rgn, pack_settings);
src->copy_to(pbo, pbo_offset, format, type, 0, src_rgn, pack_settings);
}
if (auto error = glGetError())

View file

@ -175,7 +175,7 @@ namespace gl
m_id = GL_NONE;
}
void texture::copy_from(const void* src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings)
void texture::copy_from(const rsx::io_buffer& src, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings)
{
ensure(m_samples <= 1, "Transfer operations are unsupported on multisampled textures.");
@ -185,30 +185,30 @@ namespace gl
{
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);
DSA_CALL(TextureSubImage1D, m_id, GL_TEXTURE_1D, level, region.x, region.width, static_cast<GLenum>(format), static_cast<GLenum>(type), src.data());
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);
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.data());
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);
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.data());
break;
}
case GL_TEXTURE_CUBE_MAP:
{
if (get_driver_caps().ARB_direct_state_access_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);
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.data());
}
else
{
rsx_log.warning("Cubemap upload via texture::copy_from is halfplemented!");
auto ptr = static_cast<const u8*>(src);
auto ptr = static_cast<const u8*>(src.data());
const auto end = std::min(6u, region.z + region.depth);
for (unsigned face = region.z; face < end; ++face)
{
@ -221,22 +221,25 @@ namespace gl
}
}
void texture::copy_from(buffer& buf, u32 gl_format_type, u32 offset, u32 length)
void texture::copy_from(buffer& buf, GLsizeiptr offset, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings)
{
ensure(m_samples <= 1, "Transfer operations are unsupported on multisampled textures.");
if (get_target() != target::textureBuffer)
fmt::throw_exception("OpenGL error: texture cannot copy from buffer");
buf.bind(buffer::target::pixel_unpack);
DSA_CALL(TextureBufferRange, m_id, GL_TEXTURE_BUFFER, gl_format_type, buf.id(), offset, length);
const rsx::io_buffer src{ reinterpret_cast<void*>(static_cast<uintptr_t>(offset)), buf.size() - offset };
copy_from(src, format, type, level, region, pixel_settings);
}
void texture::copy_from(buffer_view& view)
{
copy_from(*view.value(), view.format(), view.offset(), view.range());
if (get_target() != target::textureBuffer)
fmt::throw_exception("OpenGL error: texture cannot copy from buffer");
DSA_CALL(TextureBufferRange, m_id, GL_TEXTURE_BUFFER, view.format(), view.value()->id(), 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
void texture::copy_to(const rsx::io_buffer& dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const
{
ensure(m_samples <= 1, "Transfer operations are unsupported on multisampled textures.");
@ -247,14 +250,14 @@ namespace gl
region.width == m_width && region.height == m_height && region.depth == m_depth)
{
if (caps.ARB_direct_state_access_supported)
glGetTextureImage(m_id, level, static_cast<GLenum>(format), static_cast<GLenum>(type), s32{ smax }, dst);
glGetTextureImage(m_id, level, static_cast<GLenum>(format), static_cast<GLenum>(type), dst.size<GLsizei>(), dst.data());
else
glGetTextureImageEXT(m_id, static_cast<GLenum>(m_target), level, static_cast<GLenum>(format), static_cast<GLenum>(type), dst);
glGetTextureImageEXT(m_id, static_cast<GLenum>(m_target), level, static_cast<GLenum>(format), static_cast<GLenum>(type), dst.data());
}
else if (caps.ARB_direct_state_access_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);
static_cast<GLenum>(format), static_cast<GLenum>(type), s32{ smax }, dst.data());
}
else
{
@ -269,6 +272,16 @@ namespace gl
}
}
void texture::copy_to(buffer& buf, GLsizeiptr offset, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const
{
ensure(offset < buf.size(), "PBO write is out of range");
buf.bind(buffer::target::pixel_pack);
const rsx::io_buffer dst{ reinterpret_cast<void*>(static_cast<uintptr_t>(offset)), buf.size() - offset };
copy_to(dst, format, type, level, region, pixel_settings);
}
void texture_view::create(texture* data, GLenum target, GLenum sized_format, const subresource_range& range, const GLenum* argb_swizzle)
{
m_target = target;

View file

@ -4,6 +4,7 @@
#include "Utilities/geometry.h"
#include "Emu/RSX/Common/TextureUtils.h"
#include "Emu/RSX/Common/io_buffer.h"
//using enum rsx::format_class;
using namespace ::rsx::format_class_;
@ -321,22 +322,24 @@ namespace gl
}
// 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(const rsx::io_buffer& 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& buf, GLsizeiptr offset, texture::format format, texture::type type, int level, const coord3u region, const pixel_unpack_settings& pixel_settings);
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;
void copy_to(const rsx::io_buffer& dst, texture::format format, texture::type type, int level, const coord3u& region, const pixel_pack_settings& pixel_settings) const;
void copy_to(buffer& buf, GLsizeiptr offset, 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)
void copy_from(const rsx::io_buffer& 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
void copy_to(const rsx::io_buffer& 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);

View file

@ -5,6 +5,7 @@
#include "Emu/localized_string.h"
#include <memory>
#include <span>
// Definitions for common UI controls and their routines
namespace rsx
@ -39,6 +40,9 @@ namespace rsx
image_info_base() {}
virtual ~image_info_base() {}
virtual const u8* get_data() const = 0;
virtual usz size_bytes() const { return static_cast<usz>(w * h * bpp); }
std::span<const u8> as_span() const { return { get_data(), size_bytes() }; }
};
struct image_info : public image_info_base
@ -56,6 +60,7 @@ namespace rsx
void load_data(const std::vector<u8>& bytes, bool grayscaled = false);
const u8* get_data() const override { return channels == 4 ? data : data_grey.empty() ? nullptr : data_grey.data(); }
usz size_bytes() const override { return data_grey.size(); }
};
struct resource_config