// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.h"
#include <memory>
#include "base/memory/ptr_util.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.h"
#include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
#include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrContextThreadSafeProxy.h"
#include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
#include "ui/gl/scoped_restore_texture.h"
namespace gpu {
DXGISwapChainOverlayImageRepresentation::
DXGISwapChainOverlayImageRepresentation(SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker)
: OverlayImageRepresentation(manager, backing, tracker) {}
DXGISwapChainOverlayImageRepresentation::
~DXGISwapChainOverlayImageRepresentation() = default;
std::optional<gl::DCLayerOverlayImage>
DXGISwapChainOverlayImageRepresentation::GetDCLayerOverlayImage() {
return static_cast<DXGISwapChainImageBacking*>(backing())
->GetDCLayerOverlayImage();
}
bool DXGISwapChainOverlayImageRepresentation::BeginReadAccess(
gfx::GpuFenceHandle& acquire_fence) {
// For the time being, let's use present interval 0.
const bool should_synchronize_present_with_vblank = false;
bool success = static_cast<DXGISwapChainImageBacking*>(backing())->Present(
should_synchronize_present_with_vblank);
return success;
}
void DXGISwapChainOverlayImageRepresentation::EndReadAccess(
gfx::GpuFenceHandle release_fence) {}
GLTexturePassthroughDXGISwapChainBufferRepresentation::
GLTexturePassthroughDXGISwapChainBufferRepresentation(
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker,
scoped_refptr<D3DImageBacking::GLTextureHolder> texture_holder)
: GLTexturePassthroughImageRepresentation(manager, backing, tracker),
texture_holder_(std::move(texture_holder)) {}
const scoped_refptr<gles2::TexturePassthrough>&
GLTexturePassthroughDXGISwapChainBufferRepresentation::GetTexturePassthrough(
int plane_index) {
DCHECK_EQ(plane_index, 0);
return texture_holder_->texture_passthrough();
}
GLTexturePassthroughDXGISwapChainBufferRepresentation::
~GLTexturePassthroughDXGISwapChainBufferRepresentation() = default;
bool GLTexturePassthroughDXGISwapChainBufferRepresentation::BeginAccess(
GLenum mode) {
return true;
}
void GLTexturePassthroughDXGISwapChainBufferRepresentation::EndAccess() {}
// static
std::unique_ptr<SkiaGLImageRepresentationDXGISwapChain>
SkiaGLImageRepresentationDXGISwapChain::Create(
std::unique_ptr<GLTextureImageRepresentationBase> gl_representation,
scoped_refptr<SharedContextState> context_state,
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker) {
GrBackendTexture backend_texture;
GLFormatDesc format_desc = context_state->GetGLFormatCaps().ToGLFormatDesc(
backing->format(), /*plane_index=*/0);
if (!GetGrBackendTexture(
context_state->feature_info(),
gl_representation->GetTextureBase()->target(), backing->size(),
gl_representation->GetTextureBase()->service_id(),
format_desc.storage_internal_format,
context_state->gr_context()->threadSafeProxy(), &backend_texture)) {
return nullptr;
}
auto promise_texture = GrPromiseImageTexture::Make(backend_texture);
if (!promise_texture)
return nullptr;
std::vector<sk_sp<GrPromiseImageTexture>> promise_textures = {
promise_texture};
return base::WrapUnique(new SkiaGLImageRepresentationDXGISwapChain(
std::move(gl_representation), std::move(promise_textures),
std::move(context_state), manager, backing, tracker));
}
SkiaGLImageRepresentationDXGISwapChain::SkiaGLImageRepresentationDXGISwapChain(
std::unique_ptr<GLTextureImageRepresentationBase> gl_representation,
std::vector<sk_sp<GrPromiseImageTexture>> promise_textures,
scoped_refptr<SharedContextState> context_state,
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker)
: SkiaGLImageRepresentation(std::move(gl_representation),
std::move(promise_textures),
std::move(context_state),
manager,
backing,
tracker) {}
SkiaGLImageRepresentationDXGISwapChain::
~SkiaGLImageRepresentationDXGISwapChain() = default;
std::vector<sk_sp<SkSurface>>
SkiaGLImageRepresentationDXGISwapChain::BeginWriteAccess(
int final_msaa_count,
const SkSurfaceProps& surface_props,
const gfx::Rect& update_rect,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
std::unique_ptr<skgpu::MutableTextureState>* end_state) {
std::vector<sk_sp<SkSurface>> surfaces =
SkiaGLImageRepresentation::BeginWriteAccess(
final_msaa_count, surface_props, update_rect, begin_semaphores,
end_semaphores, end_state);
if (!surfaces.empty()) {
if (!static_cast<DXGISwapChainImageBacking*>(backing())
->DidBeginWriteAccess(update_rect)) {
return {};
}
}
return surfaces;
}
void SkiaGLImageRepresentationDXGISwapChain::EndWriteAccess() {
SkiaGLImageRepresentation::EndWriteAccess();
// For FLIP_SEQUENTIAL swap chains, a successful present will unbind the back
// buffer from the graphics pipeline. The state caching layers in Skia/ANGLE
// are unaware of our Present1 calls and incorrectly assume the back buffer
// texture remains bound. To work around this, we'll recreate the SkSurfaces
// for every draw to ensure that Skia/ANGLE will rebind out back buffer.
// See:
// https://learn.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgiswapchain1-present1#remarks
//
// This only needs to happen on Present, but we have it on EndWriteAccess for
// convenience. It's possible to have multiple draws per Present, but we
// assume that 1:1 is the most common case.
SkiaGLImageRepresentation::ClearCachedSurfaces();
}
DawnRepresentationDXGISwapChain::DawnRepresentationDXGISwapChain(
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker,
wgpu::Device device,
wgpu::BackendType backend_type)
: DawnImageRepresentation(manager, backing, tracker), device_(device) {
DCHECK(device_);
}
DawnRepresentationDXGISwapChain::~DawnRepresentationDXGISwapChain() {
EndAccess();
}
wgpu::Texture DawnRepresentationDXGISwapChain::BeginAccess(
wgpu::TextureUsage usage,
wgpu::TextureUsage internal_usage,
const gfx::Rect& update_rect) {
auto* swapchain_backing = static_cast<DXGISwapChainImageBacking*>(backing());
texture_ = swapchain_backing->BeginAccessDawn(device_, usage, internal_usage,
update_rect);
return texture_;
}
wgpu::Texture DawnRepresentationDXGISwapChain::BeginAccess(
wgpu::TextureUsage usage,
wgpu::TextureUsage internal_usage) {
NOTREACHED();
}
void DawnRepresentationDXGISwapChain::EndAccess() {
if (!texture_) {
return;
}
auto* swapchain_backing = static_cast<DXGISwapChainImageBacking*>(backing());
swapchain_backing->EndAccessDawn(device_, std::move(texture_));
}
} // namespace gpu