void XFBEncoder::DecodeToTexture(D3DTexture2D* dst_texture, const u8* src, u32 src_width, u32 src_height) { _assert_msg_(VIDEO, src_width <= MAX_XFB_WIDTH && src_height <= MAX_XFB_HEIGHT, "XFB source does not exceed maximum size"); // Copy to XFB upload buffer. Each row has to be done separately due to pitch differences. u32 buffer_pitch = ROUND_UP(src_width / 2 * sizeof(u32), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); m_upload_buffer->AllocateSpaceInBuffer(buffer_pitch * src_height, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); for (u32 row = 0; row < src_height; row++) { const u8* row_src = src + (src_width * 2) * row; u8* row_dst = reinterpret_cast<u8*>(m_upload_buffer->GetCPUAddressOfCurrentAllocation()) + buffer_pitch * row; memcpy(row_dst, row_src, src_width * 2); } // Copy from upload buffer to intermediate YUYV texture. D3D12_PLACED_SUBRESOURCE_FOOTPRINT src_footprint = { m_upload_buffer->GetOffsetOfCurrentAllocation(),{ DXGI_FORMAT_R8G8B8A8_UNORM, src_width / 2, src_height, 1, buffer_pitch } }; CD3DX12_TEXTURE_COPY_LOCATION src_location(m_upload_buffer->GetBuffer(), src_footprint); CD3DX12_TEXTURE_COPY_LOCATION dst_location(m_yuyv_texture->GetTex(), 0); CD3DX12_BOX src_box(0, 0, src_width / 2, src_height); m_yuyv_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_DEST); D3D::current_command_list->CopyTextureRegion(&dst_location, 0, 0, 0, &src_location, &src_box); // Convert YUYV texture to RGBA texture with pixel shader. CD3DX12_RECT src_texture_rect(0, 0, src_width / 2, src_height); dst_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET); D3D::current_command_list->OMSetRenderTargets(1, &dst_texture->GetRTV(), FALSE, nullptr); D3D::SetViewportAndScissor(0, 0, src_width, src_height); D3D::DrawShadedTexQuad( m_yuyv_texture, &src_texture_rect, XFB_TEXTURE_WIDTH, XFB_TEXTURE_HEIGHT, StaticShaderCache::GetXFBDecodePixelShader(), StaticShaderCache::GetSimpleVertexShader(), StaticShaderCache::GetSimpleVertexShaderInputLayout(), {}, 0, DXGI_FORMAT_R8G8B8A8_UNORM, false, false); // XFB source textures are expected to be in shader resource state. dst_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); }
void DXStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst, const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer, u32 dst_level) { ASSERT(m_type == StagingTextureType::Upload); ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight()); ASSERT(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= GetWidth() && src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= GetHeight()); ASSERT(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetWidth() && dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst->GetHeight()); if (IsMapped()) DXStagingTexture::Unmap(); if (static_cast<u32>(src_rect.GetWidth()) == dst->GetWidth() && static_cast<u32>(src_rect.GetHeight()) == dst->GetHeight()) { D3D::context->CopySubresourceRegion( static_cast<const DXTexture*>(dst)->GetD3DTexture(), D3D11CalcSubresource(dst_level, dst_layer, dst->GetLevels()), 0, 0, 0, m_tex, 0, nullptr); } else { CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1); D3D::context->CopySubresourceRegion( static_cast<const DXTexture*>(dst)->GetD3DTexture(), D3D11CalcSubresource(dst_level, dst_layer, dst->GetLevels()), static_cast<u32>(dst_rect.left), static_cast<u32>(dst_rect.top), 0, m_tex, 0, &src_box); } }
void DXStagingTexture::CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect, u32 src_layer, u32 src_level, const MathUtil::Rectangle<int>& dst_rect) { ASSERT(m_type == StagingTextureType::Readback || m_type == StagingTextureType::Mutable); ASSERT(src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight()); ASSERT(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src->GetWidth() && src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src->GetHeight()); ASSERT(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= m_config.width && dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= m_config.height); if (IsMapped()) DXStagingTexture::Unmap(); if (static_cast<u32>(src_rect.GetWidth()) == GetWidth() && static_cast<u32>(src_rect.GetHeight()) == GetHeight()) { // Copy whole resource, needed for depth textures. D3D::context->CopySubresourceRegion( m_tex, 0, 0, 0, 0, static_cast<const DXTexture*>(src)->GetD3DTexture(), D3D11CalcSubresource(src_level, src_layer, src->GetLevels()), nullptr); } else { CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1); D3D::context->CopySubresourceRegion( m_tex, 0, static_cast<u32>(dst_rect.left), static_cast<u32>(dst_rect.top), 0, static_cast<const DXTexture*>(src)->GetD3DTexture(), D3D11CalcSubresource(src_level, src_layer, src->GetLevels()), &src_box); } m_needs_flush = true; }
bool DXTexture::Save(const std::string& filename, unsigned int level) { // We can't dump compressed textures currently (it would mean drawing them to a RGBA8 // framebuffer, and saving that). TextureCache does not call Save for custom textures // anyway, so this is fine for now. _assert_(m_config.format == AbstractTextureFormat::RGBA8); // Create a staging/readback texture with the dimensions of the specified mip level. u32 mip_width = std::max(m_config.width >> level, 1u); u32 mip_height = std::max(m_config.height >> level, 1u); CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, mip_width, mip_height, 1, 1, 0, D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ); ID3D11Texture2D* staging_texture; HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &staging_texture); if (FAILED(hr)) { WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast<u32>(hr)); return false; } // Copy the selected mip level to the staging texture. CD3D11_BOX src_box(0, 0, 0, mip_width, mip_height, 1); D3D::context->CopySubresourceRegion(staging_texture, 0, 0, 0, 0, m_texture->GetTex(), D3D11CalcSubresource(level, 0, m_config.levels), &src_box); // Map the staging texture to client memory, and encode it as a .png image. D3D11_MAPPED_SUBRESOURCE map; hr = D3D::context->Map(staging_texture, 0, D3D11_MAP_READ, 0, &map); if (FAILED(hr)) { WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast<u32>(hr)); staging_texture->Release(); return false; } bool encode_result = TextureToPng(reinterpret_cast<u8*>(map.pData), map.RowPitch, filename, mip_width, mip_height); D3D::context->Unmap(staging_texture, 0); staging_texture->Release(); return encode_result; }
void DXStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst, const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer, u32 dst_level) { _assert_(m_type == StagingTextureType::Upload); _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight()); _assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= m_config.width && src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= m_config.height); _assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetConfig().width && dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst->GetConfig().height); if (IsMapped()) DXStagingTexture::Unmap(); CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1); D3D::context->CopySubresourceRegion( static_cast<const DXTexture*>(dst)->GetRawTexIdentifier()->GetTex(), D3D11CalcSubresource(dst_level, dst_layer, dst->GetConfig().levels), static_cast<u32>(dst_rect.left), static_cast<u32>(dst_rect.top), 0, m_tex, 0, &src_box); }
void DXStagingTexture::CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect, u32 src_layer, u32 src_level, const MathUtil::Rectangle<int>& dst_rect) { _assert_(m_type == StagingTextureType::Readback); _assert_(src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight()); _assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src->GetConfig().width && src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src->GetConfig().height); _assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= m_config.width && dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= m_config.height); if (IsMapped()) DXStagingTexture::Unmap(); CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1); D3D::context->CopySubresourceRegion( m_tex, 0, static_cast<u32>(dst_rect.left), static_cast<u32>(dst_rect.top), 0, static_cast<const DXTexture*>(src)->GetRawTexIdentifier()->GetTex(), D3D11CalcSubresource(src_level, src_layer, src->GetConfig().levels), &src_box); m_needs_flush = true; }
void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase* source, const MathUtil::Rectangle<int>& src_rect, const MathUtil::Rectangle<int>& dst_rect) { const TCacheEntry* srcentry = reinterpret_cast<const TCacheEntry*>(source); if (src_rect.GetWidth() == dst_rect.GetWidth() && src_rect.GetHeight() == dst_rect.GetHeight()) { // These assertions should hold true unless the base code is passing us sizes too large, in // which case it should be fixed instead. _assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= source->config.width && static_cast<u32>(src_rect.GetHeight()) <= source->config.height, "Source rect is too large for CopyRectangleFromTexture"); _assert_msg_(VIDEO, static_cast<u32>(dst_rect.GetWidth()) <= config.width && static_cast<u32>(dst_rect.GetHeight()) <= config.height, "Dest rect is too large for CopyRectangleFromTexture"); CD3DX12_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, srcentry->config.layers); D3D12_TEXTURE_COPY_LOCATION dst_location = CD3DX12_TEXTURE_COPY_LOCATION(m_texture->GetTex12(), 0); D3D12_TEXTURE_COPY_LOCATION src_location = CD3DX12_TEXTURE_COPY_LOCATION(srcentry->m_texture->GetTex12(), 0); m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_DEST); srcentry->m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_COPY_SOURCE); D3D::current_command_list->CopyTextureRegion(&dst_location, dst_rect.left, dst_rect.top, 0, &src_location, &src_box); m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); srcentry->m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); return; } else if (!config.rendertarget) { return; } D3D::SetViewportAndScissor(dst_rect.left, dst_rect.top, dst_rect.GetWidth(), dst_rect.GetHeight()); m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET); D3D::current_command_list->OMSetRenderTargets(1, &m_texture->GetRTV12(), FALSE, nullptr); D3D::SetLinearCopySampler(); D3D12_RECT src_rc; src_rc.left = src_rect.left; src_rc.right = src_rect.right; src_rc.top = src_rect.top; src_rc.bottom = src_rect.bottom; D3D::DrawShadedTexQuad( srcentry->m_texture, &src_rc, srcentry->config.width, srcentry->config.height, StaticShaderCache::GetColorCopyPixelShader(false), StaticShaderCache::GetSimpleVertexShader(), StaticShaderCache::GetSimpleVertexShaderInputLayout(), D3D12_SHADER_BYTECODE(), 1.0, 0, DXGI_FORMAT_R8G8B8A8_UNORM, false, m_texture->GetMultisampled()); m_texture->TransitionToResourceState(D3D::current_command_list, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); FramebufferManager::GetEFBColorTexture()->TransitionToResourceState( D3D::current_command_list, D3D12_RESOURCE_STATE_RENDER_TARGET); FramebufferManager::GetEFBDepthTexture()->TransitionToResourceState( D3D::current_command_list, D3D12_RESOURCE_STATE_DEPTH_WRITE); g_renderer->RestoreAPIState(); }
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); }
boost::shared_ptr<hier::BoxOverlap> OuterfaceGeometry::doOverlap( const OuterfaceGeometry& dst_geometry, const OuterfaceGeometry& src_geometry, const hier::Box& src_mask, const hier::Box& fill_box, const bool overwrite_interior, const hier::Transformation& transformation, const hier::BoxContainer& dst_restrict_boxes) { const tbox::Dimension& dim(src_mask.getDim()); std::vector<hier::BoxContainer> dst_boxes(dim.getValue()); // Perform a quick-and-dirty intersection to see if the boxes might overlap hier::Box src_box(src_geometry.d_box); src_box.grow(src_geometry.d_ghosts); src_box = src_box * src_mask; transformation.transform(src_box); hier::Box dst_ghost(dst_geometry.getBox()); dst_ghost.grow(dst_geometry.getGhosts()); // Compute the intersection (if any) for each of the face directions const hier::IntVector one_vector(dim, 1); const hier::Box quick_check( hier::Box::grow(src_box, one_vector) * hier::Box::grow(dst_ghost, one_vector)); if (!quick_check.empty()) { hier::Box mask_shift(src_mask); transformation.transform(mask_shift); for (tbox::Dimension::dir_t d = 0; d < dim.getValue(); ++d) { const hier::Box dst_face( FaceGeometry::toFaceBox(dst_geometry.getBox(), d)); const hier::Box src_face( FaceGeometry::toFaceBox(src_box, d)); const hier::Box fill_face( FaceGeometry::toFaceBox(fill_box, d)); const hier::Box together(dst_face * src_face * fill_face); if (!together.empty()) { const hier::Box msk_face( FaceGeometry::toFaceBox(mask_shift, d)); hier::Box low_dst_face(dst_face); low_dst_face.setUpper(0, low_dst_face.lower(0)); hier::Box hig_dst_face(dst_face); hig_dst_face.setLower(0, hig_dst_face.upper(0)); // Add lower face intersection (if any) to the box list hier::Box low_src_face(src_face); low_src_face.setUpper(0, low_src_face.lower(0)); hier::Box low_low_overlap(low_src_face * msk_face * low_dst_face); if (!low_low_overlap.empty()) { dst_boxes[d].pushBack(low_low_overlap); } hier::Box low_hig_overlap(low_src_face * msk_face * hig_dst_face); if (!low_hig_overlap.empty()) { dst_boxes[d].pushBack(low_hig_overlap); } // Add upper face intersection (if any) to the box list hier::Box hig_src_face(src_face); hig_src_face.setLower(0, hig_src_face.upper(0)); //-ghosts; hier::Box hig_low_overlap(hig_src_face * msk_face * low_dst_face); if (!hig_low_overlap.empty()) { dst_boxes[d].pushBack(hig_low_overlap); } hier::Box hig_hig_overlap(hig_src_face * msk_face * hig_dst_face); if (!hig_hig_overlap.empty()) { dst_boxes[d].pushBack(hig_hig_overlap); } // Take away the interior of over_write interior is not set if (!overwrite_interior) { dst_boxes[d].removeIntersections( FaceGeometry::toFaceBox(dst_geometry.getBox(), d)); } } // if (!together.empty()) if (!dst_restrict_boxes.empty() && !dst_boxes[d].empty()) { hier::BoxContainer face_restrict_boxes; for (hier::BoxContainer::const_iterator b = dst_restrict_boxes.begin(); b != dst_restrict_boxes.end(); ++b) { face_restrict_boxes.pushBack(FaceGeometry::toFaceBox(*b, d)); } dst_boxes[d].intersectBoxes(face_restrict_boxes); } dst_boxes[d].coalesce(); } // loop over dim } // if (!quick_check.empty()) // Create the face overlap data object using the boxes and source shift return boost::make_shared<FaceOverlap>(dst_boxes, transformation); }