void XFBEncoder::EncodeTextureToRam(u8* dst, u32 dst_pitch, u32 dst_height, D3DTexture2D* src_texture, const TargetRectangle& src_rect, u32 src_width, u32 src_height, float gamma) { // src_rect is in native coordinates // dst_pitch is in words u32 dst_width = dst_pitch / 2; u32 dst_texture_width = dst_width / 2; _assert_msg_(VIDEO, dst_width <= MAX_XFB_WIDTH && dst_height <= MAX_XFB_HEIGHT, "XFB destination does not exceed maximum size"); // Encode parameters constant buffer used by shader struct EncodeParameters { float src_rect[4]; float texel_size[4]; }; EncodeParameters parameters = { { static_cast<float>(src_rect.left) / static_cast<float>(src_width), static_cast<float>(src_rect.top) / static_cast<float>(src_height), static_cast<float>(src_rect.right) / static_cast<float>(src_width), static_cast<float>(src_rect.bottom) / static_cast<float>(src_height) }, { 1.0f / static_cast<float>(src_width), 1.0f / static_cast<float>(src_height), 0.0f, 0.0f } }; m_encode_params_buffer->AllocateSpaceInBuffer(sizeof(parameters), D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT); memcpy(m_encode_params_buffer->GetCPUAddressOfCurrentAllocation(), ¶meters, sizeof(parameters)); // Convert RGBA texture to YUYV intermediate texture. // Performs downscaling through a linear filter. Probably not ideal, but it's not going to look perfect anyway. CD3DX12_RECT src_texture_rect(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom); m_yuyv_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET); D3D::current_command_list->OMSetRenderTargets(1, &m_yuyv_texture->GetRTV(), FALSE, nullptr); D3D::current_command_list->SetGraphicsRootConstantBufferView(DESCRIPTOR_TABLE_PS_CBVONE, m_encode_params_buffer->GetGPUAddressOfCurrentAllocation()); D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_PS_CBV, true); D3D::SetViewportAndScissor(0, 0, dst_texture_width, dst_height); D3D::SetLinearCopySampler(); D3D::DrawShadedTexQuad( src_texture, &src_texture_rect, src_rect.GetWidth(), src_rect.GetHeight(), StaticShaderCache::GetXFBEncodePixelShader(), StaticShaderCache::GetSimpleVertexShader(), StaticShaderCache::GetSimpleVertexShaderInputLayout(), {}, 0, DXGI_FORMAT_R8G8B8A8_UNORM, false, false); // Copy from YUYV intermediate texture to readback buffer. It's likely the pitch here is going to be different to dst_pitch. u32 readback_pitch = ROUND_UP(dst_width * sizeof(u16), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); D3D12_PLACED_SUBRESOURCE_FOOTPRINT dst_footprint = { 0,{ DXGI_FORMAT_R8G8B8A8_UNORM, dst_texture_width, dst_height, 1, readback_pitch } }; CD3DX12_TEXTURE_COPY_LOCATION dst_location(m_readback_buffer, dst_footprint); CD3DX12_TEXTURE_COPY_LOCATION src_location(m_yuyv_texture->GetTex(), 0); CD3DX12_BOX src_box(0, 0, dst_texture_width, dst_height); m_yuyv_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_SOURCE); D3D::current_command_list->CopyTextureRegion(&dst_location, 0, 0, 0, &src_location, &src_box); // Wait until the GPU completes the copy. Resets back to known state automatically. D3D::command_list_mgr->ExecuteQueuedWork(true); // Copy from the readback buffer to dst. // Can't be done as one memcpy due to pitch difference. void* readback_texture_map; D3D12_RANGE read_range = { 0, readback_pitch * dst_height }; CheckHR(m_readback_buffer->Map(0, &read_range, &readback_texture_map)); for (u32 row = 0; row < dst_height; row++) { const u8* row_src = reinterpret_cast<u8*>(readback_texture_map) + readback_pitch * row; u8* row_dst = dst + dst_pitch * row; memcpy(row_dst, row_src, std::min(dst_pitch, readback_pitch)); } D3D12_RANGE write_range = {}; m_readback_buffer->Unmap(0, &write_range); }
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); }
void D3DPostProcessor::CopyTexture(const TargetRectangle& dst_rect, uintptr_t dst_tex, const TargetRectangle& src_rect, uintptr_t src_tex, const TargetSize& src_size, int src_layer, bool is_depth_texture, bool force_shader_copy) { D3DTexture2D* dst_texture = reinterpret_cast<D3DTexture2D*>(dst_tex); D3DTexture2D* src_texture = reinterpret_cast<D3DTexture2D*>(src_tex); // If the dimensions are the same, we can copy instead of using a shader. bool scaling = (dst_rect.GetWidth() != src_rect.GetWidth() || dst_rect.GetHeight() != src_rect.GetHeight()); if (!scaling && !force_shader_copy) { D3D12_BOX srcbox = { static_cast<UINT>(src_rect.left), static_cast<UINT>(src_rect.top), 0, static_cast<UINT>(src_rect.right), static_cast<UINT>(src_rect.bottom), 1 }; D3D12_TEXTURE_COPY_LOCATION dst = CD3DX12_TEXTURE_COPY_LOCATION(dst_texture->GetTex(), 0); D3D12_TEXTURE_COPY_LOCATION src = CD3DX12_TEXTURE_COPY_LOCATION(src_texture->GetTex(), 0); dst_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_DEST); src_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_SOURCE); if (src_layer < 0) { // Copy all layers for (unsigned int layer = 0; layer < FramebufferManager::GetEFBLayers(); layer++) { src.SubresourceIndex = D3D12CalcSubresource(0, layer, 0, 1, FramebufferManager::GetEFBLayers()); dst.SubresourceIndex = src.SubresourceIndex; D3D::current_command_list->CopyTextureRegion(&dst, dst_rect.left, dst_rect.top, 0, &src, &srcbox); } } else { // Copy single layer to layer 0 D3D::current_command_list->CopyTextureRegion(&dst, dst_rect.left, dst_rect.top, 0, &src, &srcbox); } } else { D3D::SetViewportAndScissor(dst_rect.left, dst_rect.top, dst_rect.GetWidth(), dst_rect.GetHeight()); dst_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET); D3D::current_command_list->OMSetRenderTargets(1, &dst_texture->GetRTV(), FALSE, nullptr); if (scaling) D3D::SetLinearCopySampler(); else D3D::SetPointCopySampler(); D3D12_SHADER_BYTECODE bytecode = {}; D3D::DrawShadedTexQuad(src_texture, src_rect.AsRECT(), src_size.width, src_size.height, StaticShaderCache::GetColorCopyPixelShader(false), StaticShaderCache::GetSimpleVertexShader(), StaticShaderCache::GetSimpleVertexShaderInputLayout(), (src_layer < 0) ? StaticShaderCache::GetCopyGeometryShader() : bytecode, 0, dst_texture->GetFormat(), false, dst_texture->GetMultisampled()); } }