GLuint FramebufferManager::GetEFBDepthTexture(const EFBRectangle& sourceRc) { if (m_msaaSamples <= 1) { return m_efbDepth; } else { // Transfer the EFB to a resolved texture. TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc); targetRc.ClampUL(0, 0, m_targetWidth, m_targetHeight); // Resolve. for (unsigned int i = 0; i < m_EFBLayers; i++) { glBindFramebuffer(GL_READ_FRAMEBUFFER, m_efbFramebuffer[i]); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolvedFramebuffer[i]); glBlitFramebuffer(targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, GL_DEPTH_BUFFER_BIT, GL_NEAREST); } // Return to EFB. glBindFramebuffer(GL_FRAMEBUFFER, m_efbFramebuffer[0]); return m_resolvedDepthTexture; } }
GLuint FramebufferManager::GetEFBDepthTexture(const EFBRectangle& sourceRc) { if (m_msaaSamples <= 1) { return m_efbDepth; } else { // Transfer the EFB to a resolved texture. EXT_framebuffer_blit is // required. TargetRectangle targetRc = g_renderer->ConvertEFBRectangle(sourceRc); targetRc.ClampLL(0, 0, m_targetWidth, m_targetHeight); // Resolve. glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_efbFramebuffer); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, m_resolvedFramebuffer); glBlitFramebufferEXT( targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, targetRc.left, targetRc.top, targetRc.right, targetRc.bottom, GL_DEPTH_BUFFER_BIT, GL_NEAREST ); // Return to EFB. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_efbFramebuffer); return m_resolvedDepthTexture; } }
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); }
size_t PSTextureEncoder::Encode(u8* dst, unsigned int dstFormat, PEControl::PixelFormat srcFormat, const EFBRectangle& srcRect, bool isIntensity, bool scaleByHalf) { if (!m_ready) // Make sure we initialized OK return 0; // Clamp srcRect to 640x528. BPS: The Strike tries to encode an 800x600 // texture, which is invalid. EFBRectangle correctSrc = srcRect; correctSrc.ClampUL(0, 0, EFB_WIDTH, EFB_HEIGHT); // Validate source rect size if (correctSrc.GetWidth() <= 0 || correctSrc.GetHeight() <= 0) return 0; HRESULT hr; unsigned int blockW = BLOCK_WIDTHS[dstFormat]; unsigned int blockH = BLOCK_HEIGHTS[dstFormat]; // Round up source dims to multiple of block size unsigned int actualWidth = correctSrc.GetWidth() / (scaleByHalf ? 2 : 1); actualWidth = (actualWidth + blockW-1) & ~(blockW-1); unsigned int actualHeight = correctSrc.GetHeight() / (scaleByHalf ? 2 : 1); actualHeight = (actualHeight + blockH-1) & ~(blockH-1); unsigned int numBlocksX = actualWidth/blockW; unsigned int numBlocksY = actualHeight/blockH; unsigned int cacheLinesPerRow; if (dstFormat == 0x6) // RGBA takes two cache lines per block; all others take one cacheLinesPerRow = numBlocksX*2; else cacheLinesPerRow = numBlocksX; _assert_msg_(VIDEO, cacheLinesPerRow*32 <= MAX_BYTES_PER_BLOCK_ROW, "cache lines per row sanity check"); unsigned int totalCacheLines = cacheLinesPerRow * numBlocksY; _assert_msg_(VIDEO, totalCacheLines*32 <= MAX_BYTES_PER_ENCODE, "total encode size sanity check"); size_t encodeSize = 0; // Reset API g_renderer->ResetAPIState(); // Set up all the state for EFB encoding { D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, FLOAT(cacheLinesPerRow * 8), FLOAT(numBlocksY)); D3D::context->RSSetViewports(1, &vp); EFBRectangle fullSrcRect; fullSrcRect.left = 0; fullSrcRect.top = 0; fullSrcRect.right = EFB_WIDTH; fullSrcRect.bottom = EFB_HEIGHT; TargetRectangle targetRect = g_renderer->ConvertEFBRectangle(fullSrcRect); D3D::context->OMSetRenderTargets(1, &m_outRTV, nullptr); ID3D11ShaderResourceView* pEFB = (srcFormat == PEControl::Z24) ? FramebufferManager::GetResolvedEFBDepthTexture()->GetSRV() : // FIXME: Instead of resolving EFB, it would be better to pick out a // single sample from each pixel. The game may break if it isn't // expecting the blurred edges around multisampled shapes. FramebufferManager::GetResolvedEFBColorTexture()->GetSRV(); EFBEncodeParams params; params.SrcLeft = correctSrc.left; params.SrcTop = correctSrc.top; params.DestWidth = actualWidth; params.ScaleFactor = scaleByHalf ? 2 : 1; D3D::context->UpdateSubresource(m_encodeParams, 0, nullptr, ¶ms, 0, 0); D3D::stateman->SetPixelConstants(m_encodeParams); // Use linear filtering if (bScaleByHalf), use point filtering otherwise if (scaleByHalf) D3D::SetLinearCopySampler(); else D3D::SetPointCopySampler(); D3D::drawShadedTexQuad(pEFB, targetRect.AsRECT(), Renderer::GetTargetWidth(), Renderer::GetTargetHeight(), SetStaticShader(dstFormat, srcFormat, isIntensity, scaleByHalf), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout()); // Copy to staging buffer D3D11_BOX srcBox = CD3D11_BOX(0, 0, 0, cacheLinesPerRow * 8, numBlocksY, 1); D3D::context->CopySubresourceRegion(m_outStage, 0, 0, 0, 0, m_out, 0, &srcBox); // Transfer staging buffer to GameCube/Wii RAM D3D11_MAPPED_SUBRESOURCE map = { 0 }; hr = D3D::context->Map(m_outStage, 0, D3D11_MAP_READ, 0, &map); CHECK(SUCCEEDED(hr), "map staging buffer (0x%x)", hr); u8* src = (u8*)map.pData; for (unsigned int y = 0; y < numBlocksY; ++y) { memcpy(dst, src, cacheLinesPerRow*32); dst += bpmem.copyMipMapStrideChannels*32; src += map.RowPitch; } D3D::context->Unmap(m_outStage, 0); encodeSize = bpmem.copyMipMapStrideChannels*32 * numBlocksY; } // Restore API g_renderer->RestoreAPIState(); D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV()); return encodeSize; }
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 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()); } }
void D3DPostProcessingShader::Draw(PostProcessor* p, const TargetRectangle& dst_rect, const TargetSize& dst_size, uintptr_t dst_tex, const TargetRectangle& src_rect, const TargetSize& src_size, uintptr_t src_tex, uintptr_t src_depth_tex, int src_layer, float gamma) { D3DPostProcessor* parent = reinterpret_cast<D3DPostProcessor*>(p); D3DTexture2D* dst_texture = reinterpret_cast<D3DTexture2D*>(dst_tex); D3DTexture2D* src_texture = reinterpret_cast<D3DTexture2D*>(src_tex); D3DTexture2D* src_depth_texture = reinterpret_cast<D3DTexture2D*>(src_depth_tex); _dbg_assert_(VIDEO, m_ready && m_internal_size == src_size); // Determine whether we can skip the final copy by writing directly to the output texture, if the last pass is not scaled. bool skip_final_copy = !IsLastPassScaled() && (dst_texture != src_texture || !m_last_pass_uses_color_buffer) && !m_prev_frame_enabled; // Draw each pass. PostProcessor::InputTextureSizeArray input_sizes; TargetRectangle output_rect = {}; TargetSize output_size; D3D12_CPU_DESCRIPTOR_HANDLE base_sampler_cpu; D3D12_GPU_DESCRIPTOR_HANDLE base_sampler_gpu; int required_handles = (int)(POST_PROCESSING_MAX_TEXTURE_INPUTS * m_passes.size()); DX12::D3D::sampler_descriptor_heap_mgr->AllocateGroup(required_handles, &base_sampler_cpu, &base_sampler_gpu); D3D12_CPU_DESCRIPTOR_HANDLE base_texture_cpu; D3D12_GPU_DESCRIPTOR_HANDLE base_texture_gpu; // On the first texture in the group, we need to allocate the space in the descriptor heap. if (!DX12::D3D::gpu_descriptor_heap_mgr->AllocateTemporary(required_handles, &base_texture_cpu, &base_texture_gpu)) { // Kick command buffer before attempting to allocate again. This is slow. D3D::command_list_mgr->ExecuteQueuedWork(); if (!D3D::gpu_descriptor_heap_mgr->AllocateTemporary(required_handles, &base_texture_cpu, &base_texture_gpu)) { PanicAlert("Failed to allocate temporary descriptors."); return; } } MapAndUpdateConfigurationBuffer(); for (size_t pass_index = 0; pass_index < m_passes.size(); pass_index++) { const RenderPassData& pass = m_passes[pass_index]; bool is_last_pass = (pass_index == m_last_pass_index); if (!pass.enabled) continue; D3D12_CPU_DESCRIPTOR_HANDLE sampler_cpu = { base_sampler_cpu.ptr + pass_index * POST_PROCESSING_MAX_TEXTURE_INPUTS * D3D::sampler_descriptor_size}; D3D12_GPU_DESCRIPTOR_HANDLE sampler_gpu = { base_sampler_gpu.ptr + pass_index * POST_PROCESSING_MAX_TEXTURE_INPUTS * D3D::sampler_descriptor_size }; D3D12_CPU_DESCRIPTOR_HANDLE texture_cpu = { base_texture_cpu.ptr + pass_index * POST_PROCESSING_MAX_TEXTURE_INPUTS * D3D::resource_descriptor_size }; D3D12_GPU_DESCRIPTOR_HANDLE texture_gpu = { base_texture_gpu.ptr + pass_index * POST_PROCESSING_MAX_TEXTURE_INPUTS * D3D::resource_descriptor_size }; // Bind inputs to pipeline for (size_t i = 0; i < pass.inputs.size(); i++) { const InputBinding& input = pass.inputs[i]; D3D12_CPU_DESCRIPTOR_HANDLE textureDestDescriptor; D3DTexture2D* input_texture = nullptr; textureDestDescriptor.ptr = texture_cpu.ptr + i * D3D::resource_descriptor_size; switch (input.type) { case POST_PROCESSING_INPUT_TYPE_COLOR_BUFFER: input_texture = src_texture; input_sizes[i] = src_size; break; case POST_PROCESSING_INPUT_TYPE_DEPTH_BUFFER: input_texture = src_depth_texture; input_sizes[i] = src_size; break; case POST_PROCESSING_INPUT_TYPE_PASS_FRAME_OUTPUT: if (m_prev_frame_enabled) { input_texture = reinterpret_cast<D3DTexture2D*>(GetPrevColorFrame(input.frame_index)->GetInternalObject()); input_sizes[i] = m_prev_frame_size; } break; case POST_PROCESSING_INPUT_TYPE_PASS_DEPTH_FRAME_OUTPUT: if (m_prev_depth_enabled) { input_texture = reinterpret_cast<D3DTexture2D*>(GetPrevDepthFrame(input.frame_index)->GetInternalObject()); input_sizes[i] = m_prev_depth_frame_size; } break; default: TextureCacheBase::TCacheEntryBase* i_texture = input.texture != nullptr ? input.texture : input.prev_texture; if (i_texture != nullptr) { input_texture = reinterpret_cast<D3DTexture2D*>(i_texture->GetInternalObject()); input_sizes[i] = input.size; } else { input_texture = src_texture; input_sizes[i] = src_size; } break; } if (input_texture) { input_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); } DX12::D3D::device->CopyDescriptorsSimple( 1, textureDestDescriptor, input_texture != nullptr ? input_texture->GetSRVCPUShadow() : DX12::D3D::null_srv_cpu_shadow, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV ); D3D12_CPU_DESCRIPTOR_HANDLE destinationDescriptor; destinationDescriptor.ptr = sampler_cpu.ptr + i * D3D::sampler_descriptor_size; DX12::D3D::device->CopyDescriptorsSimple( 1, destinationDescriptor, parent->GetSamplerHandle(static_cast<UINT>(input.texture_sampler) - 1), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER ); } D3D::current_command_list->SetGraphicsRootDescriptorTable(DESCRIPTOR_TABLE_PS_SRV, texture_gpu); D3D::current_command_list->SetGraphicsRootDescriptorTable(DESCRIPTOR_TABLE_PS_SAMPLER, sampler_gpu); D3D::command_list_mgr->SetCommandListDirtyState(COMMAND_LIST_STATE_SAMPLERS, true); // If this is the last pass and we can skip the final copy, write directly to output texture. if (is_last_pass && skip_final_copy) { // The target rect may differ from the source. output_rect = dst_rect; output_size = dst_size; dst_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET); D3D::current_command_list->OMSetRenderTargets(1, &dst_texture->GetRTV(), FALSE, nullptr); } else { output_rect = PostProcessor::ScaleTargetRectangle(API_D3D11, src_rect, pass.output_scale); output_size = pass.output_size; reinterpret_cast<D3DTexture2D*>(pass.output_texture->GetInternalObject())->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET); D3D::current_command_list->OMSetRenderTargets(1, &reinterpret_cast<D3DTexture2D*>(pass.output_texture->GetInternalObject())->GetRTV(), FALSE, nullptr); } // Set viewport based on target rect D3D::SetViewportAndScissor(output_rect.left, output_rect.top, output_rect.GetWidth(), output_rect.GetHeight()); parent->MapAndUpdateUniformBuffer(input_sizes, output_rect, output_size, src_rect, src_size, src_layer, gamma); // Select geometry shader based on layers D3D12_SHADER_BYTECODE geometry_shader = {}; if (src_layer < 0 && m_internal_layers > 1) geometry_shader = parent->GetGeometryShader(); // Draw pass D3D::DrawShadedTexQuad(nullptr, src_rect.AsRECT(), src_size.width, src_size.height, reinterpret_cast<RenderPassDx12Data*>(pass.shader)->m_shader_bytecode, parent->GetVertexShader(), StaticShaderCache::GetSimpleVertexShaderInputLayout(), geometry_shader, std::max(src_layer, 0), DXGI_FORMAT_R8G8B8A8_UNORM, false, dst_texture->GetMultisampled()); } // Copy the last pass output to the target if not done already IncrementFrame(); if (m_prev_depth_enabled && src_depth_tex) { TargetRectangle dst; dst.left = 0; dst.right = m_prev_depth_frame_size.width; dst.top = 0; dst.bottom = m_prev_depth_frame_size.height; parent->CopyTexture(dst, GetPrevDepthFrame(0)->GetInternalObject(), output_rect, src_depth_tex, src_size, src_layer, true, true); } if (!skip_final_copy) { RenderPassData& final_pass = m_passes[m_last_pass_index]; if (m_prev_frame_enabled) { TargetRectangle dst; dst.left = 0; dst.right = m_prev_frame_size.width; dst.top = 0; dst.bottom = m_prev_frame_size.height; parent->CopyTexture(dst, GetPrevColorFrame(0)->GetInternalObject(), output_rect, final_pass.output_texture->GetInternalObject(), final_pass.output_size, src_layer, false, true); } parent->CopyTexture(dst_rect, dst_tex, output_rect, final_pass.output_texture->GetInternalObject(), final_pass.output_size, src_layer); } }
void PSTextureEncoder::Encode(u8* dst, const EFBCopyFormat& format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half) { if (!m_ready) // Make sure we initialized OK return; HRESULT hr; // Resolve MSAA targets before copying. // FIXME: Instead of resolving EFB, it would be better to pick out a // single sample from each pixel. The game may break if it isn't // expecting the blurred edges around multisampled shapes. ID3D11ShaderResourceView* pEFB = is_depth_copy ? FramebufferManager::GetResolvedEFBDepthTexture()->GetSRV() : FramebufferManager::GetResolvedEFBColorTexture()->GetSRV(); // Reset API g_renderer->ResetAPIState(); // Set up all the state for EFB encoding { const u32 words_per_row = bytes_per_row / sizeof(u32); D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, FLOAT(words_per_row), FLOAT(num_blocks_y)); D3D::context->RSSetViewports(1, &vp); constexpr EFBRectangle fullSrcRect(0, 0, EFB_WIDTH, EFB_HEIGHT); TargetRectangle targetRect = g_renderer->ConvertEFBRectangle(fullSrcRect); D3D::context->OMSetRenderTargets(1, &m_outRTV, nullptr); EFBEncodeParams params; params.SrcLeft = src_rect.left; params.SrcTop = src_rect.top; params.DestWidth = native_width; params.ScaleFactor = scale_by_half ? 2 : 1; D3D::context->UpdateSubresource(m_encodeParams, 0, nullptr, ¶ms, 0, 0); D3D::stateman->SetPixelConstants(m_encodeParams); // We also linear filtering for both box filtering and downsampling higher resolutions to 1x // TODO: This only produces perfect downsampling for 1.5x and 2x IR, other resolution will // need more complex down filtering to average all pixels and produce the correct result. // Also, box filtering won't be correct for anything other than 1x IR if (scale_by_half || g_ActiveConfig.iEFBScale != SCALE_1X) D3D::SetLinearCopySampler(); else D3D::SetPointCopySampler(); D3D::drawShadedTexQuad(pEFB, targetRect.AsRECT(), g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight(), GetEncodingPixelShader(format), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout()); // Copy to staging buffer D3D11_BOX srcBox = CD3D11_BOX(0, 0, 0, words_per_row, num_blocks_y, 1); D3D::context->CopySubresourceRegion(m_outStage, 0, 0, 0, 0, m_out, 0, &srcBox); // Transfer staging buffer to GameCube/Wii RAM D3D11_MAPPED_SUBRESOURCE map = {0}; hr = D3D::context->Map(m_outStage, 0, D3D11_MAP_READ, 0, &map); CHECK(SUCCEEDED(hr), "map staging buffer (0x%x)", hr); u8* src = (u8*)map.pData; u32 readStride = std::min(bytes_per_row, map.RowPitch); for (unsigned int y = 0; y < num_blocks_y; ++y) { memcpy(dst, src, readStride); dst += memory_stride; src += map.RowPitch; } D3D::context->Unmap(m_outStage, 0); } // Restore API g_renderer->RestoreAPIState(); D3D::context->OMSetRenderTargets(1, &FramebufferManager::GetEFBColorTexture()->GetRTV(), FramebufferManager::GetEFBDepthTexture()->GetDSV()); }
void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, const EFBRectangle& src_rect, bool scale_by_half) { // Resolve MSAA targets before copying. // FIXME: Instead of resolving EFB, it would be better to pick out a // single sample from each pixel. The game may break if it isn't // expecting the blurred edges around multisampled shapes. ID3D11ShaderResourceView* pEFB = params.depth ? FramebufferManager::GetResolvedEFBDepthTexture()->GetSRV() : FramebufferManager::GetResolvedEFBColorTexture()->GetSRV(); // Reset API g_renderer->ResetAPIState(); // Set up all the state for EFB encoding { const u32 words_per_row = bytes_per_row / sizeof(u32); D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, FLOAT(words_per_row), FLOAT(num_blocks_y)); D3D::context->RSSetViewports(1, &vp); constexpr EFBRectangle fullSrcRect(0, 0, EFB_WIDTH, EFB_HEIGHT); TargetRectangle targetRect = g_renderer->ConvertEFBRectangle(fullSrcRect); D3D::context->OMSetRenderTargets( 1, &static_cast<DXTexture*>(m_encoding_render_texture.get())->GetRawTexIdentifier()->GetRTV(), nullptr); EFBEncodeParams encode_params; encode_params.SrcLeft = src_rect.left; encode_params.SrcTop = src_rect.top; encode_params.DestWidth = native_width; encode_params.ScaleFactor = scale_by_half ? 2 : 1; encode_params.y_scale = params.y_scale; D3D::context->UpdateSubresource(m_encode_params, 0, nullptr, &encode_params, 0, 0); D3D::stateman->SetPixelConstants(m_encode_params); // We also linear filtering for both box filtering and downsampling higher resolutions to 1x // TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more // complex down filtering to average all pixels and produce the correct result. // Also, box filtering won't be correct for anything other than 1x IR if (scale_by_half || g_renderer->GetEFBScale() != 1 || params.y_scale > 1.0f) D3D::SetLinearCopySampler(); else D3D::SetPointCopySampler(); D3D::drawShadedTexQuad(pEFB, targetRect.AsRECT(), g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight(), GetEncodingPixelShader(params), VertexShaderCache::GetSimpleVertexShader(), VertexShaderCache::GetSimpleInputLayout()); // Copy to staging buffer MathUtil::Rectangle<int> copy_rect(0, 0, words_per_row, num_blocks_y); m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0, copy_rect); m_encoding_readback_texture->Flush(); if (m_encoding_readback_texture->Map()) { m_encoding_readback_texture->ReadTexels(copy_rect, dst, memory_stride); m_encoding_readback_texture->Unmap(); } } // Restore API FramebufferManager::BindEFBRenderTarget(); g_renderer->RestoreAPIState(); }