Custom blitter for final resolve.

This commit is contained in:
Ben Vanik 2015-03-07 10:17:09 -08:00
parent 2bce5ba0d9
commit d72610ba1b
11 changed files with 307 additions and 35 deletions

View File

@ -0,0 +1,198 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/gpu/gl4/blitter.h"
#include <string>
#include "poly/assert.h"
#include "poly/math.h"
namespace xe {
namespace gpu {
namespace gl4 {
extern "C" GLEWContext* glewGetContext();
extern "C" WGLEWContext* wglewGetContext();
Blitter::Blitter()
: vertex_program_(0),
fragment_program_(0),
pipeline_(0),
vbo_(0),
vao_(0),
nearest_sampler_(0),
linear_sampler_(0) {}
Blitter::~Blitter() = default;
bool Blitter::Initialize() {
const std::string header =
"\n\
#version 450 \n\
#extension GL_ARB_explicit_uniform_location : require \n\
#extension GL_ARB_shading_language_420pack : require \n\
precision highp float; \n\
precision highp int; \n\
layout(std140, column_major) uniform; \n\
layout(std430, column_major) buffer; \n\
struct VertexData { \n\
vec2 uv; \n\
}; \n\
";
const std::string vs_source = header +
"\n\
layout(location = 0) uniform vec4 src_uv_params; \n\
out gl_PerVertex { \n\
vec4 gl_Position; \n\
float gl_PointSize; \n\
float gl_ClipDistance[]; \n\
}; \n\
struct VertexFetch { \n\
vec2 pos; \n\
};\n\
layout(location = 0) in VertexFetch vfetch; \n\
layout(location = 0) out VertexData vtx; \n\
void main() { \n\
gl_Position = vec4(vfetch.pos.xy * vec2(2.0, 2.0) - vec2(1.0, 1.0), 0.0, 1.0); \n\
vtx.uv = vfetch.pos.xy * src_uv_params.zw + src_uv_params.xy; \n\
} \n\
";
const std::string fs_source = header +
"\n\
layout(location = 1) uniform sampler2D src_texture; \n\
layout(location = 0) in VertexData vtx; \n\
layout(location = 0) out vec4 oC; \n\
void main() { \n\
vec4 color = texture(src_texture, vtx.uv); \n\
oC = color; \n\
} \n\
";
auto vs_source_str = vs_source.c_str();
vertex_program_ = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &vs_source_str);
auto fs_source_str = fs_source.c_str();
fragment_program_ =
glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fs_source_str);
char log[2048];
GLsizei log_length;
glGetProgramInfoLog(vertex_program_, 2048, &log_length, log);
glCreateProgramPipelines(1, &pipeline_);
glUseProgramStages(pipeline_, GL_VERTEX_SHADER_BIT, vertex_program_);
glUseProgramStages(pipeline_, GL_FRAGMENT_SHADER_BIT, fragment_program_);
glCreateBuffers(1, &vbo_);
static const GLfloat vbo_data[] = {
0, 0, 1, 0, 0, 1, 1, 1,
};
glNamedBufferStorage(vbo_, sizeof(vbo_data), vbo_data, 0);
glCreateVertexArrays(1, &vao_);
glEnableVertexArrayAttrib(vao_, 0);
glVertexArrayAttribBinding(vao_, 0, 0);
glVertexArrayAttribFormat(vao_, 0, 2, GL_FLOAT, GL_FALSE, 0);
glVertexArrayVertexBuffer(vao_, 0, vbo_, 0, sizeof(GLfloat) * 2);
glCreateSamplers(1, &nearest_sampler_);
glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glSamplerParameteri(nearest_sampler_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(nearest_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glCreateSamplers(1, &linear_sampler_);
glSamplerParameteri(linear_sampler_, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(linear_sampler_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(linear_sampler_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return true;
}
void Blitter::Shutdown() {
if (vertex_program_) {
glDeleteProgram(vertex_program_);
}
if (fragment_program_) {
glDeleteProgram(fragment_program_);
}
if (pipeline_) {
glDeleteProgramPipelines(1, &pipeline_);
}
if (vbo_) {
glDeleteBuffers(1, &vbo_);
}
if (vao_) {
glDeleteVertexArrays(1, &vao_);
}
if (nearest_sampler_) {
glDeleteSamplers(1, &nearest_sampler_);
}
if (linear_sampler_) {
glDeleteSamplers(1, &linear_sampler_);
}
}
void Blitter::Draw(GLuint src_texture, uint32_t src_x, uint32_t src_y,
uint32_t src_width, uint32_t src_height, GLenum filter) {
glDisablei(GL_BLEND, 0);
glDisable(GL_DEPTH_TEST);
glBindProgramPipeline(pipeline_);
glBindVertexArray(vao_);
glBindTextures(0, 1, &src_texture);
switch (filter) {
default:
case GL_NEAREST:
glBindSampler(0, nearest_sampler_);
break;
case GL_LINEAR:
glBindSampler(0, linear_sampler_);
break;
}
// TODO(benvanik): avoid this?
GLint src_texture_width;
glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_WIDTH,
&src_texture_width);
GLint src_texture_height;
glGetTextureLevelParameteriv(src_texture, 0, GL_TEXTURE_HEIGHT,
&src_texture_height);
glProgramUniform4f(vertex_program_, 0, src_x / float(src_texture_width),
src_y / float(src_texture_height),
src_width / float(src_texture_width),
src_height / float(src_texture_height));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindProgramPipeline(0);
glBindVertexArray(0);
GLuint zero = 0;
glBindTextures(0, 1, &zero);
glBindSampler(0, 0);
}
void Blitter::BlitTexture2D(GLuint src_texture, uint32_t src_x, uint32_t src_y,
uint32_t src_width, uint32_t src_height,
uint32_t dest_x, uint32_t dest_y,
uint32_t dest_width, uint32_t dest_height,
GLenum filter) {
glViewport(dest_x, dest_y, dest_width, dest_height);
Draw(src_texture, src_x, src_y, src_width, src_height, filter);
}
void Blitter::CopyTexture2D(GLuint src_texture, uint32_t src_x, uint32_t src_y,
uint32_t src_width, uint32_t src_height,
uint32_t dest_texture, uint32_t dest_x,
uint32_t dest_y, uint32_t dest_width,
uint32_t dest_height, GLenum filter) {
glViewport(dest_x, dest_y, dest_width, dest_height);
//
}
} // namespace gl4
} // namespace gpu
} // namespace xe

View File

@ -0,0 +1,57 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_GL4_BLITTER_H_
#define XENIA_GPU_GL4_BLITTER_H_
#include <memory>
#include "third_party/GL/glew.h"
#include "third_party/GL/wglew.h"
namespace xe {
namespace gpu {
namespace gl4 {
class Blitter {
public:
Blitter();
~Blitter();
bool Initialize();
void Shutdown();
void BlitTexture2D(GLuint src_texture, uint32_t src_x, uint32_t src_y,
uint32_t src_width, uint32_t src_height, uint32_t dest_x,
uint32_t dest_y, uint32_t dest_width, uint32_t dest_height,
GLenum filter);
void CopyTexture2D(GLuint src_texture, uint32_t src_x, uint32_t src_y,
uint32_t src_width, uint32_t src_height,
uint32_t dest_texture, uint32_t dest_x, uint32_t dest_y,
uint32_t dest_width, uint32_t dest_height, GLenum filter);
private:
void Draw(GLuint src_texture, uint32_t src_x, uint32_t src_y,
uint32_t src_width, uint32_t src_height, GLenum filter);
GLuint vertex_program_;
GLuint fragment_program_;
GLuint pipeline_;
GLuint vbo_;
GLuint vao_;
GLuint nearest_sampler_;
GLuint linear_sampler_;
};
} // namespace gl4
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_GL4_BLITTER_H_

View File

@ -75,6 +75,7 @@ CommandProcessor::CommandProcessor(GL4GraphicsSystem* graphics_system)
active_vertex_shader_(nullptr), active_vertex_shader_(nullptr),
active_pixel_shader_(nullptr), active_pixel_shader_(nullptr),
active_framebuffer_(nullptr), active_framebuffer_(nullptr),
last_framebuffer_texture_(0),
point_list_geometry_program_(0), point_list_geometry_program_(0),
rect_list_geometry_program_(0), rect_list_geometry_program_(0),
quad_list_geometry_program_(0), quad_list_geometry_program_(0),
@ -566,13 +567,7 @@ void CommandProcessor::IssueSwap() {
// TODO(benvanik): handle dirty cases (resolved to sysmem, touched). // TODO(benvanik): handle dirty cases (resolved to sysmem, touched).
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// HACK: just use whatever our current framebuffer is. // HACK: just use whatever our current framebuffer is.
if (active_framebuffer_) { swap_params.framebuffer_texture = last_framebuffer_texture_;
swap_params.framebuffer = active_framebuffer_->framebuffer;
// TODO(benvanik): pick the right one?
swap_params.attachment = GL_COLOR_ATTACHMENT0;
} else {
swap_params.framebuffer = 0;
}
// Guess frontbuffer dimensions. // Guess frontbuffer dimensions.
// Command buffer seems to set these right before the XE_SWAP. // Command buffer seems to set these right before the XE_SWAP.
@ -2513,7 +2508,7 @@ bool CommandProcessor::IssueCopy() {
glNamedFramebufferReadBuffer(source_framebuffer->framebuffer, glNamedFramebufferReadBuffer(source_framebuffer->framebuffer,
GL_COLOR_ATTACHMENT0 + copy_src_select); GL_COLOR_ATTACHMENT0 + copy_src_select);
// TODO(benvanik): RAW copy. // TODO(benvanik): RAW copy.
texture_cache_.CopyReadBufferTexture( last_framebuffer_texture_ = texture_cache_.CopyReadBufferTexture(
copy_dest_base, x, y, w, h, copy_dest_base, x, y, w, h,
ColorFormatToTextureFormat(copy_dest_format), ColorFormatToTextureFormat(copy_dest_format),
copy_dest_swap ? true : false); copy_dest_swap ? true : false);
@ -2539,7 +2534,7 @@ bool CommandProcessor::IssueCopy() {
GL_COLOR_ATTACHMENT0 + copy_src_select); GL_COLOR_ATTACHMENT0 + copy_src_select);
// Either copy the readbuffer into an existing texture or create a new // Either copy the readbuffer into an existing texture or create a new
// one in the cache so we can service future upload requests. // one in the cache so we can service future upload requests.
texture_cache_.CopyReadBufferTexture( last_framebuffer_texture_ = texture_cache_.CopyReadBufferTexture(
copy_dest_base, x, y, w, h, copy_dest_base, x, y, w, h,
ColorFormatToTextureFormat(copy_dest_format), ColorFormatToTextureFormat(copy_dest_format),
copy_dest_swap ? true : false); copy_dest_swap ? true : false);

View File

@ -40,8 +40,7 @@ struct SwapParameters {
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
GLuint framebuffer; GLuint framebuffer_texture;
GLenum attachment;
}; };
enum class SwapMode { enum class SwapMode {
@ -254,6 +253,7 @@ class CommandProcessor {
GL4Shader* active_vertex_shader_; GL4Shader* active_vertex_shader_;
GL4Shader* active_pixel_shader_; GL4Shader* active_pixel_shader_;
CachedFramebuffer* active_framebuffer_; CachedFramebuffer* active_framebuffer_;
GLuint last_framebuffer_texture_;
std::vector<CachedFramebuffer> cached_framebuffers_; std::vector<CachedFramebuffer> cached_framebuffers_;
std::vector<CachedColorRenderTarget> cached_color_render_targets_; std::vector<CachedColorRenderTarget> cached_color_render_targets_;

View File

@ -258,11 +258,10 @@ void GL4GraphicsSystem::SwapHandler(const SwapParameters& swap_params) {
// Swap requested. Synchronously post a request to the loop so that // Swap requested. Synchronously post a request to the loop so that
// we do the swap in the right thread. // we do the swap in the right thread.
control_->SynchronousRepaint([&]() { control_->SynchronousRepaint([&]() {
glBlitNamedFramebuffer(swap_params.framebuffer, 0, swap_params.x, control_->context()->blitter()->BlitTexture2D(
swap_params.y, swap_params.x + swap_params.width, swap_params.framebuffer_texture, swap_params.x, swap_params.y,
swap_params.y + swap_params.height, 0, 0, swap_params.width, swap_params.height, 0, 0, control_->width(),
control_->width(), control_->height(), control_->height(), GL_LINEAR);
GL_COLOR_BUFFER_BIT, GL_LINEAR);
}); });
} }

View File

@ -244,14 +244,14 @@ bool GL4ProfilerDisplay::SetupShaders() {
const std::string header = const std::string header =
"\n\ "\n\
#version 450 \n\ #version 450 \n\
#extension GL_ARB_bindless_texture : require\n\ #extension GL_ARB_bindless_texture : require \n\
#extension GL_ARB_explicit_uniform_location : require\n\ #extension GL_ARB_explicit_uniform_location : require \n\
#extension GL_ARB_shading_language_420pack : require\n\ #extension GL_ARB_shading_language_420pack : require \n\
precision highp float; \n\ precision highp float; \n\
precision highp int;\n\ precision highp int; \n\
layout(std140, column_major) uniform;\n\ layout(std140, column_major) uniform; \n\
layout(std430, column_major) buffer;\n\ layout(std430, column_major) buffer; \n\
struct VertexData {\n\ struct VertexData { \n\
vec4 color; \n\ vec4 color; \n\
vec2 uv; \n\ vec2 uv; \n\
};\n\ };\n\

View File

@ -36,7 +36,9 @@ GLContext::GLContext(HWND hwnd, HGLRC glrc)
} }
GLContext::~GLContext() { GLContext::~GLContext() {
wglMakeCurrent(nullptr, nullptr); MakeCurrent();
blitter_.Shutdown();
ClearCurrent();
if (glrc_) { if (glrc_) {
wglDeleteContext(glrc_); wglDeleteContext(glrc_);
} }
@ -119,6 +121,12 @@ bool GLContext::Initialize(HWND hwnd) {
SetupDebugging(); SetupDebugging();
if (!blitter_.Initialize()) {
PLOGE("Unable to initialize blitter");
ClearCurrent();
return false;
}
ClearCurrent(); ClearCurrent();
return true; return true;
@ -169,6 +177,11 @@ std::unique_ptr<GLContext> GLContext::CreateShared() {
SetupDebugging(); SetupDebugging();
if (!new_context->blitter_.Initialize()) {
PLOGE("Unable to initialize blitter");
return nullptr;
}
new_context->ClearCurrent(); new_context->ClearCurrent();
return new_context; return new_context;

View File

@ -12,6 +12,8 @@
#include <memory> #include <memory>
#include "xenia/gpu/gl4/blitter.h"
#include "third_party/GL/glew.h" #include "third_party/GL/glew.h"
#include "third_party/GL/wglew.h" #include "third_party/GL/wglew.h"
@ -34,6 +36,8 @@ class GLContext {
bool MakeCurrent(); bool MakeCurrent();
void ClearCurrent(); void ClearCurrent();
Blitter* blitter() { return &blitter_; }
private: private:
void SetupDebugging(); void SetupDebugging();
void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity,
@ -48,6 +52,8 @@ class GLContext {
GLEWContext glew_context_; GLEWContext glew_context_;
WGLEWContext wglew_context_; WGLEWContext wglew_context_;
Blitter blitter_;
}; };
struct GLContextLock { struct GLContextLock {

View File

@ -1,6 +1,8 @@
# Copyright 2014 Ben Vanik. All Rights Reserved. # Copyright 2014 Ben Vanik. All Rights Reserved.
{ {
'sources': [ 'sources': [
'blitter.cc',
'blitter.h',
'circular_buffer.cc', 'circular_buffer.cc',
'circular_buffer.h', 'circular_buffer.h',
'command_processor.cc', 'command_processor.cc',

View File

@ -526,11 +526,11 @@ TextureCache::TextureEntry* TextureCache::LookupAddress(uint32_t guest_address,
return nullptr; return nullptr;
} }
void TextureCache::CopyReadBufferTexture(uint32_t guest_address, uint32_t x, GLuint TextureCache::CopyReadBufferTexture(uint32_t guest_address, uint32_t x,
uint32_t y, uint32_t width, uint32_t y, uint32_t width,
uint32_t height, uint32_t height,
TextureFormat format, TextureFormat format,
bool swap_channels) { bool swap_channels) {
// See if we have used a texture at this address before. If we have, we can // See if we have used a texture at this address before. If we have, we can
// reuse it. // reuse it.
// TODO(benvanik): better lookup matching format/etc? // TODO(benvanik): better lookup matching format/etc?
@ -546,7 +546,7 @@ void TextureCache::CopyReadBufferTexture(uint32_t guest_address, uint32_t x,
memory_->CancelWriteWatch(texture_entry->write_watch_handle); memory_->CancelWriteWatch(texture_entry->write_watch_handle);
texture_entry->write_watch_handle = 0; texture_entry->write_watch_handle = 0;
} }
return; return texture_entry->handle;
} }
// Check pending read buffer textures (for multiple resolves with no // Check pending read buffer textures (for multiple resolves with no
@ -558,14 +558,14 @@ void TextureCache::CopyReadBufferTexture(uint32_t guest_address, uint32_t x,
entry->height == height && entry->format == format) { entry->height == height && entry->format == format) {
// Found an existing entry - just reupload. // Found an existing entry - just reupload.
glCopyTextureSubImage2D(entry->handle, 0, 0, 0, x, y, width, height); glCopyTextureSubImage2D(entry->handle, 0, 0, 0, x, y, width, height);
return; return entry->handle;
} }
} }
const auto& config = texture_configs[uint32_t(format)]; const auto& config = texture_configs[uint32_t(format)];
if (config.format == GL_INVALID_ENUM) { if (config.format == GL_INVALID_ENUM) {
assert_always("Unhandled destination texture format"); assert_always("Unhandled destination texture format");
return; return 0;
} }
// Need to create a new texture. // Need to create a new texture.
@ -584,7 +584,9 @@ void TextureCache::CopyReadBufferTexture(uint32_t guest_address, uint32_t x,
glTextureStorage2D(entry->handle, 1, config.internal_format, width, height); glTextureStorage2D(entry->handle, 1, config.internal_format, width, height);
glCopyTextureSubImage2D(entry->handle, 0, 0, 0, x, y, width, height); glCopyTextureSubImage2D(entry->handle, 0, 0, 0, x, y, width, height);
GLuint handle = entry->handle;
read_buffer_textures_.push_back(entry.release()); read_buffer_textures_.push_back(entry.release());
return handle;
} }
void TextureCache::EvictTexture(TextureEntry* entry) { void TextureCache::EvictTexture(TextureEntry* entry) {

View File

@ -57,9 +57,9 @@ class TextureCache {
TextureEntryView* Demand(const TextureInfo& texture_info, TextureEntryView* Demand(const TextureInfo& texture_info,
const SamplerInfo& sampler_info); const SamplerInfo& sampler_info);
void CopyReadBufferTexture(uint32_t guest_address, uint32_t x, uint32_t y, GLuint CopyReadBufferTexture(uint32_t guest_address, uint32_t x, uint32_t y,
uint32_t width, uint32_t height, uint32_t width, uint32_t height,
TextureFormat format, bool swap_channels); TextureFormat format, bool swap_channels);
private: private:
struct ReadBufferTexture { struct ReadBufferTexture {