mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-05 06:21:26 +12:00
- 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
656 lines
19 KiB
C++
656 lines
19 KiB
C++
#include "stdafx.h"
|
|
#include "GLHelpers.h"
|
|
#include "GLTexture.h"
|
|
#include "GLCompute.h"
|
|
#include "util/logs.hpp"
|
|
|
|
#include "../Common/simple_array.hpp"
|
|
#include <unordered_map>
|
|
|
|
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)
|
|
{
|
|
fence_obj.check_signaled();
|
|
}
|
|
|
|
GLenum draw_mode(rsx::primitive_type in)
|
|
{
|
|
switch (in)
|
|
{
|
|
case rsx::primitive_type::points: return GL_POINTS;
|
|
case rsx::primitive_type::lines: return GL_LINES;
|
|
case rsx::primitive_type::line_loop: return GL_LINE_LOOP;
|
|
case rsx::primitive_type::line_strip: return GL_LINE_STRIP;
|
|
case rsx::primitive_type::triangles: return GL_TRIANGLES;
|
|
case rsx::primitive_type::triangle_strip: return GL_TRIANGLE_STRIP;
|
|
case rsx::primitive_type::triangle_fan: return GL_TRIANGLE_FAN;
|
|
case rsx::primitive_type::quads: return GL_TRIANGLES;
|
|
case rsx::primitive_type::quad_strip: return GL_TRIANGLE_STRIP;
|
|
case rsx::primitive_type::polygon: return GL_TRIANGLES;
|
|
default:
|
|
fmt::throw_exception("unknown primitive type");
|
|
}
|
|
}
|
|
|
|
void destroy_compute_tasks()
|
|
{
|
|
for (auto& [key, prog] : g_compute_tasks)
|
|
{
|
|
prog->destroy();
|
|
}
|
|
|
|
g_compute_tasks.clear();
|
|
}
|
|
|
|
// https://www.khronos.org/opengl/wiki/Debug_Output
|
|
void APIENTRY log_debug(GLenum source, GLenum type, GLuint id,
|
|
GLenum severity, GLsizei /*length*/, const GLchar* message,
|
|
const void* /*user_param*/)
|
|
{
|
|
// Message source
|
|
std::string str_source;
|
|
switch (source)
|
|
{
|
|
// Calls to the OpenGL API
|
|
case GL_DEBUG_SOURCE_API:
|
|
str_source = "API";
|
|
break;
|
|
// Calls to a window-system API
|
|
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
|
str_source = "WINDOW_SYSTEM";
|
|
break;
|
|
// A compiler for a shading language
|
|
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
|
str_source = "SHADER_COMPILER";
|
|
break;
|
|
// An application associated with OpenGL
|
|
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
|
str_source = "THIRD_PARTY";
|
|
break;
|
|
// Generated by the user of this application
|
|
case GL_DEBUG_SOURCE_APPLICATION:
|
|
str_source = "APPLICATION";
|
|
break;
|
|
// Some source that isn't one of these
|
|
case GL_DEBUG_SOURCE_OTHER:
|
|
str_source = "OTHER";
|
|
break;
|
|
// Not on documentation
|
|
default:
|
|
str_source = "UNKNOWN";
|
|
rsx_log.error("log_debug(source=%d): Unknown message source", source);
|
|
}
|
|
|
|
// Message type
|
|
std::string str_type;
|
|
switch (type)
|
|
{
|
|
// An error, typically from the API
|
|
case GL_DEBUG_TYPE_ERROR:
|
|
str_type = "ERROR";
|
|
break;
|
|
// Some behavior marked deprecated has been used
|
|
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
|
str_type = "DEPRECATED";
|
|
break;
|
|
// Something has invoked undefined behavior
|
|
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
|
str_type = "UNDEFINED";
|
|
break;
|
|
// Some functionality the user relies upon is not portable
|
|
case GL_DEBUG_TYPE_PORTABILITY:
|
|
str_type = "PORTABILITY";
|
|
break;
|
|
// Code has triggered possible performance issues
|
|
case GL_DEBUG_TYPE_PERFORMANCE:
|
|
str_type = "PERFORMANCE";
|
|
break;
|
|
// Command stream annotation
|
|
case GL_DEBUG_TYPE_MARKER:
|
|
str_type = "MARKER";
|
|
break;
|
|
// Group pushing
|
|
case GL_DEBUG_TYPE_PUSH_GROUP:
|
|
str_type = "PUSH_GROUP";
|
|
break;
|
|
// foo
|
|
case GL_DEBUG_TYPE_POP_GROUP:
|
|
str_type = "POP_GROUP";
|
|
break;
|
|
// Some type that isn't one of these
|
|
case GL_DEBUG_TYPE_OTHER:
|
|
str_type = "OTHER";
|
|
break;
|
|
// Not on documentation
|
|
default:
|
|
str_type = "UNKNOWN";
|
|
rsx_log.error("log_debug(type=%d): Unknown message type", type);
|
|
}
|
|
|
|
switch (severity)
|
|
{
|
|
// All OpenGL Errors, shader compilation/linking errors, or highly-dangerous undefined behavior
|
|
case GL_DEBUG_SEVERITY_HIGH:
|
|
// Major performance warnings, shader compilation/linking warnings, or the use of deprecated functionality
|
|
case GL_DEBUG_SEVERITY_MEDIUM:
|
|
rsx_log.error("[DEBUG_OUTPUT] [%s] [%s] [%d]: %s", str_source, str_type, id, message);
|
|
return;
|
|
// Redundant state change performance warning, or unimportant undefined behavior
|
|
case GL_DEBUG_SEVERITY_LOW:
|
|
rsx_log.warning("[DEBUG_OUTPUT] [%s] [%s] [%d]: %s", str_source, str_type, id, message);
|
|
return;
|
|
// Anything that isn't an error or performance issue
|
|
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
|
rsx_log.notice("[DEBUG_OUTPUT] [%s] [%s] [%d]: %s", str_source, str_type, id, message);
|
|
return;
|
|
// Not on documentation
|
|
default:
|
|
rsx_log.error("log_debug(severity=%d): Unknown severity level", severity);
|
|
rsx_log.error("[DEBUG_OUTPUT] [%s] [%s] [%d]: %s", str_source, str_type, id, message);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void enable_debugging()
|
|
{
|
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
|
glDebugMessageCallback(log_debug, nullptr);
|
|
}
|
|
|
|
const capabilities& get_driver_caps()
|
|
{
|
|
if (!g_driver_caps.initialized)
|
|
g_driver_caps.initialize();
|
|
|
|
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)
|
|
{
|
|
case rsx::primitive_type::points:
|
|
case rsx::primitive_type::lines:
|
|
case rsx::primitive_type::line_loop:
|
|
case rsx::primitive_type::line_strip:
|
|
case rsx::primitive_type::triangles:
|
|
case rsx::primitive_type::triangle_strip:
|
|
case rsx::primitive_type::triangle_fan:
|
|
case rsx::primitive_type::quad_strip:
|
|
return true;
|
|
case rsx::primitive_type::quads:
|
|
case rsx::primitive_type::polygon:
|
|
return false;
|
|
default:
|
|
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;
|
|
}
|
|
}
|