// Should be scale free. void DecodeToTexture(u32 xfbAddr, int srcWidth, int srcHeight, GLuint destTexture) { u8* srcAddr = Memory::GetPointer(xfbAddr); if (!srcAddr) { WARN_LOG(VIDEO, "Tried to decode from invalid memory address"); return; } g_renderer->ResetAPIState(); // reset any game specific settings OpenGL_BindAttributelessVAO(); // switch to texture converter frame buffer // attach destTexture as color destination FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[1]); FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_ARRAY, destTexture, 0); // activate source texture // set srcAddr as data for source texture glActiveTexture(GL_TEXTURE9); glBindTexture(GL_TEXTURE_2D, s_srcTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcWidth / 2, srcHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, srcAddr); g_sampler_cache->BindNearestSampler(9); glViewport(0, 0, srcWidth, srcHeight); s_yuyvToRgbProgram.Bind(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); FramebufferManager::SetFramebuffer(0); g_renderer->RestoreAPIState(); }
void FramebufferManager::ReinterpretPixelData(unsigned int convtype) { g_renderer->ResetAPIState(); OpenGL_BindAttributelessVAO(); GLuint src_texture = 0; // We aren't allowed to render and sample the same texture in one draw call, // so we have to create a new texture and overwrite it completely. // To not allocate one big texture every time, we've allocated two on // initialization and just swap them here: src_texture = m_efbColor; m_efbColor = m_efbColorSwap; m_efbColorSwap = src_texture; FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_textureType, m_efbColor, 0); glViewport(0, 0, m_targetWidth, m_targetHeight); glActiveTexture(GL_TEXTURE9); glBindTexture(m_textureType, src_texture); g_sampler_cache->BindNearestSampler(9); m_pixel_format_shaders[convtype ? 1 : 0].Bind(); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindTexture(m_textureType, 0); g_renderer->RestoreAPIState(); }
static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line_size, u32 dstHeight, u32 writeStride, bool linearFilter) { // switch to texture converter frame buffer // attach render buffer as color destination FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[0]); OpenGL_BindAttributelessVAO(); // set source texture glActiveTexture(GL_TEXTURE9); glBindTexture(GL_TEXTURE_2D_ARRAY, srcTexture); if (linearFilter) g_sampler_cache->BindLinearSampler(9); else g_sampler_cache->BindNearestSampler(9); glViewport(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); int dstSize = dst_line_size * dstHeight; if ((writeStride != dst_line_size) && (dstHeight > 1)) { // writing to a texture of a different size // also copy more then one block line, so the different strides matters // copy into one pbo first, map this buffer, and then memcpy into GC memory // in this way, we only have one vram->ram transfer, but maybe a bigger // CPU overhead because of the pbo glBindBuffer(GL_PIXEL_PACK_BUFFER, s_PBO); glBufferData(GL_PIXEL_PACK_BUFFER, dstSize, nullptr, GL_STREAM_READ); glReadPixels(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); u8* pbo = (u8*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, dstSize, GL_MAP_READ_BIT); for (size_t i = 0; i < dstHeight; ++i) { memcpy(destAddr, pbo, dst_line_size); pbo += dst_line_size; destAddr += writeStride; } glUnmapBuffer(GL_PIXEL_PACK_BUFFER); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } else { glReadPixels(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, destAddr); } }
void OpenGLPostProcessing::BlitFromTexture(TargetRectangle src, TargetRectangle dst, int src_texture, int src_width, int src_height, int layer) { ApplyShader(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glViewport(dst.left, dst.bottom, dst.GetWidth(), dst.GetHeight()); OpenGL_BindAttributelessVAO(); m_shader.Bind(); glUniform4f(m_uniform_resolution, (float)src_width, (float)src_height, 1.0f / (float)src_width, 1.0f / (float)src_height); glUniform4f(m_uniform_src_rect, src.left / (float) src_width, src.bottom / (float) src_height, src.GetWidth() / (float) src_width, src.GetHeight() / (float) src_height); glUniform1ui(m_uniform_time, (GLuint)m_timer.GetTimeElapsed()); glUniform1i(m_uniform_layer, layer); if (m_config.IsDirty()) { for (auto& it : m_config.GetOptions()) { if (it.second.m_dirty) { switch (it.second.m_type) { case PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_BOOL: glUniform1i(m_uniform_bindings[it.first], it.second.m_bool_value); break; case PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_INTEGER: switch (it.second.m_integer_values.size()) { case 1: glUniform1i(m_uniform_bindings[it.first], it.second.m_integer_values[0]); break; case 2: glUniform2i(m_uniform_bindings[it.first], it.second.m_integer_values[0], it.second.m_integer_values[1]); break; case 3: glUniform3i(m_uniform_bindings[it.first], it.second.m_integer_values[0], it.second.m_integer_values[1], it.second.m_integer_values[2]); break; case 4: glUniform4i(m_uniform_bindings[it.first], it.second.m_integer_values[0], it.second.m_integer_values[1], it.second.m_integer_values[2], it.second.m_integer_values[3]); break; } break; case PostProcessingShaderConfiguration::ConfigurationOption::OptionType::OPTION_FLOAT: switch (it.second.m_float_values.size()) { case 1: glUniform1f(m_uniform_bindings[it.first], it.second.m_float_values[0]); break; case 2: glUniform2f(m_uniform_bindings[it.first], it.second.m_float_values[0], it.second.m_float_values[1]); break; case 3: glUniform3f(m_uniform_bindings[it.first], it.second.m_float_values[0], it.second.m_float_values[1], it.second.m_float_values[2]); break; case 4: glUniform4f(m_uniform_bindings[it.first], it.second.m_float_values[0], it.second.m_float_values[1], it.second.m_float_values[2], it.second.m_float_values[3]); break; } break; } it.second.m_dirty = false; } } m_config.SetDirty(false); } glActiveTexture(GL_TEXTURE9); glBindTexture(GL_TEXTURE_2D_ARRAY, src_texture); g_sampler_cache->BindLinearSampler(9); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); }
static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, int dstWidth, int dstHeight, int readStride, bool linearFilter) { // switch to texture converter frame buffer // attach render buffer as color destination FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[0]); OpenGL_BindAttributelessVAO(); // set source texture glActiveTexture(GL_TEXTURE0+9); glBindTexture(GL_TEXTURE_2D_ARRAY, srcTexture); if (linearFilter) { glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } glViewport(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // .. and then read back the results. // TODO: make this less slow. int writeStride = bpmem.copyMipMapStrideChannels * 32; int dstSize = dstWidth*dstHeight*4; int readHeight = readStride / dstWidth / 4; // 4 bytes per pixel int readLoops = dstHeight / readHeight; if (writeStride != readStride && readLoops > 1) { // writing to a texture of a different size // also copy more then one block line, so the different strides matters // copy into one pbo first, map this buffer, and then memcpy into GC memory // in this way, we only have one vram->ram transfer, but maybe a bigger // CPU overhead because of the pbo glBindBuffer(GL_PIXEL_PACK_BUFFER, s_PBO); glBufferData(GL_PIXEL_PACK_BUFFER, dstSize, nullptr, GL_STREAM_READ); glReadPixels(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); u8* pbo = (u8*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, dstSize, GL_MAP_READ_BIT); for (int i = 0; i < readLoops; i++) { memcpy(destAddr, pbo, readStride); pbo += readStride; destAddr += writeStride; } glUnmapBuffer(GL_PIXEL_PACK_BUFFER); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } else { glReadPixels(0, 0, (GLsizei)dstWidth, (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE, destAddr); } }
void TextureCache::TCacheEntry::FromRenderTarget(u32 dstAddr, unsigned int dstFormat, PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect, bool isIntensity, bool scaleByHalf, unsigned int cbufid, const float *colmat) { g_renderer->ResetAPIState(); // reset any game specific settings // Make sure to resolve anything we need to read from. const GLuint read_texture = (srcFormat == PEControl::Z24) ? FramebufferManager::ResolveAndGetDepthTarget(srcRect) : FramebufferManager::ResolveAndGetRenderTarget(srcRect); if (type != TCET_EC_DYNAMIC || g_ActiveConfig.bCopyEFBToTexture) { FramebufferManager::SetFramebuffer(framebuffer); OpenGL_BindAttributelessVAO(); glActiveTexture(GL_TEXTURE0+9); glBindTexture(GL_TEXTURE_2D_ARRAY, read_texture); glViewport(0, 0, virtual_width, virtual_height); GLuint uniform_location; if (srcFormat == PEControl::Z24) { s_DepthMatrixProgram.Bind(); if (s_DepthCbufid != cbufid) glUniform4fv(s_DepthMatrixUniform, 5, colmat); s_DepthCbufid = cbufid; uniform_location = s_DepthCopyPositionUniform; } else { s_ColorMatrixProgram.Bind(); if (s_ColorCbufid != cbufid) glUniform4fv(s_ColorMatrixUniform, 7, colmat); s_ColorCbufid = cbufid; uniform_location = s_ColorCopyPositionUniform; } TargetRectangle R = g_renderer->ConvertEFBRectangle(srcRect); glUniform4f(uniform_location, static_cast<float>(R.left), static_cast<float>(R.top), static_cast<float>(R.right), static_cast<float>(R.bottom)); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } if (false == g_ActiveConfig.bCopyEFBToTexture) { int encoded_size = TextureConverter::EncodeToRamFromTexture( addr, read_texture, srcFormat == PEControl::Z24, isIntensity, dstFormat, scaleByHalf, srcRect); u8* dst = Memory::GetPointer(addr); u64 const new_hash = GetHash64(dst,encoded_size,g_ActiveConfig.iSafeTextureCache_ColorSamples); // Mark texture entries in destination address range dynamic unless caching is enabled and the texture entry is up to date if (!g_ActiveConfig.bEFBCopyCacheEnable) TextureCache::MakeRangeDynamic(addr,encoded_size); else if (!TextureCache::Find(addr, new_hash)) TextureCache::MakeRangeDynamic(addr,encoded_size); hash = new_hash; } FramebufferManager::SetFramebuffer(0); if (g_ActiveConfig.bDumpEFBTarget) { static int count = 0; SaveTexture(StringFromFormat("%sefb_frame_%i.png", File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(), count++), GL_TEXTURE_2D_ARRAY, texture, virtual_width, virtual_height, 0); } g_renderer->RestoreAPIState(); }