| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h" |
| |
| #include "base/atomic_sequence_num.h" |
| #include "base/callback_helpers.h" |
| #include "base/optional.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "components/viz/common/frame_sinks/copy_output_request.h" |
| #include "components/viz/common/skia_helper.h" |
| #include "components/viz/service/display/output_surface_frame.h" |
| #include "components/viz/service/gl/gpu_service_impl.h" |
| #include "gpu/command_buffer/common/swap_buffers_complete_params.h" |
| #include "gpu/command_buffer/service/context_state.h" |
| #include "gpu/command_buffer/service/gr_shader_cache.h" |
| #include "gpu/command_buffer/service/mailbox_manager.h" |
| #include "gpu/command_buffer/service/scheduler.h" |
| #include "gpu/command_buffer/service/shared_image_factory.h" |
| #include "gpu/command_buffer/service/shared_image_representation.h" |
| #include "gpu/command_buffer/service/skia_utils.h" |
| #include "gpu/command_buffer/service/sync_point_manager.h" |
| #include "gpu/command_buffer/service/texture_base.h" |
| #include "gpu/command_buffer/service/texture_manager.h" |
| #include "gpu/config/gpu_preferences.h" |
| #include "gpu/ipc/common/gpu_client_ids.h" |
| #include "gpu/ipc/common/gpu_surface_lookup.h" |
| #include "gpu/ipc/service/image_transport_surface.h" |
| #include "gpu/vulkan/buildflags.h" |
| #include "third_party/skia/include/private/SkDeferredDisplayList.h" |
| #include "ui/gfx/skia_util.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_gl_api_implementation.h" |
| #include "ui/gl/gl_surface.h" |
| #include "ui/gl/gl_version_info.h" |
| #include "ui/gl/init/gl_factory.h" |
| |
| #if BUILDFLAG(ENABLE_VULKAN) |
| #include "gpu/vulkan/vulkan_implementation.h" |
| #endif |
| namespace viz { |
| namespace { |
| |
| base::AtomicSequenceNumber g_next_command_buffer_id; |
| |
| } // namespace |
| |
| SkiaOutputSurfaceImplOnGpu::SkiaOutputSurfaceImplOnGpu( |
| GpuServiceImpl* gpu_service, |
| gpu::SurfaceHandle surface_handle, |
| const DidSwapBufferCompleteCallback& did_swap_buffer_complete_callback, |
| const BufferPresentedCallback& buffer_presented_callback, |
| const ContextLostCallback& context_lost_callback) |
| : command_buffer_id_(gpu::CommandBufferId::FromUnsafeValue( |
| g_next_command_buffer_id.GetNext() + 1)), |
| gpu_service_(gpu_service), |
| surface_handle_(surface_handle), |
| did_swap_buffer_complete_callback_(did_swap_buffer_complete_callback), |
| buffer_presented_callback_(buffer_presented_callback), |
| context_lost_callback_(context_lost_callback), |
| // TODO(https://crbug.com/899905): Use a real MemoryTracker, not nullptr. |
| shared_image_representation_factory_( |
| std::make_unique<gpu::SharedImageRepresentationFactory>( |
| gpu_service_->shared_image_manager(), |
| nullptr)), |
| weak_ptr_factory_(this) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| weak_ptr_ = weak_ptr_factory_.GetWeakPtr(); |
| |
| sync_point_client_state_ = |
| gpu_service_->sync_point_manager()->CreateSyncPointClientState( |
| gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE, command_buffer_id_, |
| gpu_service_->skia_output_surface_sequence_id()); |
| |
| gpu::GpuChannelManager* channel_manager = gpu_service_->gpu_channel_manager(); |
| feature_info_ = base::MakeRefCounted<gpu::gles2::FeatureInfo>( |
| channel_manager->gpu_driver_bug_workarounds(), |
| channel_manager->gpu_feature_info()); |
| |
| if (gpu_service_->is_using_vulkan()) |
| InitializeForVulkan(); |
| else |
| InitializeForGL(); |
| } |
| |
| SkiaOutputSurfaceImplOnGpu::~SkiaOutputSurfaceImplOnGpu() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| #if BUILDFLAG(ENABLE_VULKAN) |
| if (vulkan_surface_) { |
| vulkan_surface_->Destroy(); |
| vulkan_surface_ = nullptr; |
| } |
| #endif |
| sync_point_client_state_->Destroy(); |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::Reshape( |
| const gfx::Size& size, |
| float device_scale_factor, |
| const gfx::ColorSpace& color_space, |
| bool has_alpha, |
| bool use_stencil, |
| SkSurfaceCharacterization* characterization, |
| base::WaitableEvent* event) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| base::ScopedClosureRunner scoped_runner; |
| if (event) { |
| scoped_runner.ReplaceClosure( |
| base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event))); |
| } |
| |
| if (!gpu_service_->is_using_vulkan()) { |
| if (!MakeCurrent()) |
| return; |
| gl::GLSurface::ColorSpace surface_color_space = |
| color_space == gfx::ColorSpace::CreateSCRGBLinear() |
| ? gl::GLSurface::ColorSpace::SCRGB_LINEAR |
| : gl::GLSurface::ColorSpace::UNSPECIFIED; |
| if (!gl_surface_->Resize(size, device_scale_factor, surface_color_space, |
| has_alpha)) { |
| LOG(FATAL) << "Failed to resize."; |
| // TODO(penghuang): Handle the failure. |
| } |
| DCHECK(gr_context()); |
| |
| SkSurfaceProps surface_props = |
| SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType); |
| |
| GrGLFramebufferInfo framebuffer_info; |
| framebuffer_info.fFBOID = 0; |
| framebuffer_info.fFormat = |
| gl_version_info_->is_es ? GL_BGRA8_EXT : GL_RGBA8; |
| |
| GrBackendRenderTarget render_target(size.width(), size.height(), 0, 8, |
| framebuffer_info); |
| |
| sk_surface_ = SkSurface::MakeFromBackendRenderTarget( |
| gr_context(), render_target, kBottomLeft_GrSurfaceOrigin, |
| kBGRA_8888_SkColorType, nullptr, &surface_props); |
| DCHECK(sk_surface_); |
| } else { |
| #if BUILDFLAG(ENABLE_VULKAN) |
| gfx::AcceleratedWidget accelerated_widget = gfx::kNullAcceleratedWidget; |
| #if defined(OS_ANDROID) |
| accelerated_widget = |
| gpu::GpuSurfaceLookup::GetInstance()->AcquireNativeWidget( |
| surface_handle_); |
| #else |
| accelerated_widget = surface_handle_; |
| #endif |
| if (!vulkan_surface_) { |
| auto vulkan_surface = gpu_service_->vulkan_context_provider() |
| ->GetVulkanImplementation() |
| ->CreateViewSurface(accelerated_widget); |
| if (!vulkan_surface) |
| LOG(FATAL) << "Failed to create vulkan surface."; |
| if (!vulkan_surface->Initialize( |
| gpu_service_->vulkan_context_provider()->GetDeviceQueue(), |
| gpu::VulkanSurface::DEFAULT_SURFACE_FORMAT)) { |
| LOG(FATAL) << "Failed to initialize vulkan surface."; |
| } |
| vulkan_surface_ = std::move(vulkan_surface); |
| } |
| auto old_size = vulkan_surface_->size(); |
| vulkan_surface_->SetSize(size); |
| if (vulkan_surface_->size() != old_size) { |
| // Size has been changed, we need to clear all surfaces which will be |
| // recreated later. |
| sk_surfaces_.clear(); |
| sk_surfaces_.resize(vulkan_surface_->GetSwapChain()->num_images()); |
| } |
| CreateSkSurfaceForVulkan(); |
| #else |
| NOTREACHED(); |
| #endif |
| } |
| |
| if (characterization) { |
| sk_surface_->characterize(characterization); |
| DCHECK(characterization->isValid()); |
| } |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::FinishPaintCurrentFrame( |
| std::unique_ptr<SkDeferredDisplayList> ddl, |
| std::unique_ptr<SkDeferredDisplayList> overdraw_ddl, |
| uint64_t sync_fence_release) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(ddl); |
| DCHECK(sk_surface_); |
| |
| if (!MakeCurrent()) |
| return; |
| |
| { |
| base::Optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use; |
| if (gpu_service_->gr_shader_cache()) |
| cache_use.emplace(gpu_service_->gr_shader_cache(), |
| gpu::kInProcessCommandBufferClientId); |
| sk_surface_->draw(ddl.get()); |
| gr_context()->flush(); |
| } |
| sync_point_client_state_->ReleaseFenceSync(sync_fence_release); |
| |
| if (overdraw_ddl) { |
| sk_sp<SkSurface> overdraw_surface = SkSurface::MakeRenderTarget( |
| gr_context(), overdraw_ddl->characterization(), SkBudgeted::kNo); |
| overdraw_surface->draw(overdraw_ddl.get()); |
| |
| SkPaint paint; |
| sk_sp<SkImage> overdraw_image = overdraw_surface->makeImageSnapshot(); |
| |
| sk_sp<SkColorFilter> colorFilter = SkiaHelper::MakeOverdrawColorFilter(); |
| paint.setColorFilter(colorFilter); |
| // TODO(xing.xu): move below to the thread where skia record happens. |
| sk_surface_->getCanvas()->drawImage(overdraw_image.get(), 0, 0, &paint); |
| gr_context()->flush(); |
| } |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::SwapBuffers(OutputSurfaceFrame frame) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(sk_surface_); |
| base::TimeTicks swap_start, swap_end; |
| if (!gpu_service_->is_using_vulkan()) { |
| if (!MakeCurrent()) |
| return; |
| swap_start = base::TimeTicks::Now(); |
| OnSwapBuffers(); |
| gl_surface_->SwapBuffers(frame.need_presentation_feedback |
| ? buffer_presented_callback_ |
| : base::DoNothing()); |
| swap_end = base::TimeTicks::Now(); |
| } else { |
| #if BUILDFLAG(ENABLE_VULKAN) |
| swap_start = base::TimeTicks::Now(); |
| OnSwapBuffers(); |
| auto backend = sk_surface_->getBackendRenderTarget( |
| SkSurface::kFlushRead_BackendHandleAccess); |
| GrVkImageInfo vk_image_info; |
| if (!backend.getVkImageInfo(&vk_image_info)) |
| NOTREACHED() << "Failed to get the image info."; |
| vulkan_surface_->GetSwapChain()->SetCurrentImageLayout( |
| vk_image_info.fImageLayout); |
| |
| gpu::SwapBuffersCompleteParams params; |
| params.swap_response.swap_start = base::TimeTicks::Now(); |
| params.swap_response.result = vulkan_surface_->SwapBuffers(); |
| params.swap_response.swap_end = base::TimeTicks::Now(); |
| DidSwapBuffersComplete(params); |
| |
| CreateSkSurfaceForVulkan(); |
| swap_end = base::TimeTicks::Now(); |
| #else |
| NOTREACHED(); |
| #endif |
| } |
| for (auto& latency : frame.latency_info) { |
| latency.AddLatencyNumberWithTimestamp( |
| ui::INPUT_EVENT_GPU_SWAP_BUFFER_COMPONENT, swap_start, 1); |
| latency.AddLatencyNumberWithTimestamp( |
| ui::INPUT_EVENT_LATENCY_FRAME_SWAP_COMPONENT, swap_end, 1); |
| } |
| latency_tracker_.OnGpuSwapBuffersCompleted(frame.latency_info); |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass( |
| RenderPassId id, |
| std::unique_ptr<SkDeferredDisplayList> ddl, |
| uint64_t sync_fence_release) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(ddl); |
| |
| if (!MakeCurrent()) |
| return; |
| |
| auto& surface = offscreen_surfaces_[id]; |
| SkSurfaceCharacterization characterization; |
| // TODO(penghuang): Using characterization != ddl->characterization(), when |
| // the SkSurfaceCharacterization::operator!= is implemented in Skia. |
| if (!surface || !surface->characterize(&characterization) || |
| characterization != ddl->characterization()) { |
| surface = SkSurface::MakeRenderTarget(gr_context(), ddl->characterization(), |
| SkBudgeted::kNo); |
| DCHECK(surface); |
| } |
| { |
| base::Optional<gpu::raster::GrShaderCache::ScopedCacheUse> cache_use; |
| if (gpu_service_->gr_shader_cache()) |
| cache_use.emplace(gpu_service_->gr_shader_cache(), |
| gpu::kInProcessCommandBufferClientId); |
| surface->draw(ddl.get()); |
| gr_context()->flush(); |
| } |
| sync_point_client_state_->ReleaseFenceSync(sync_fence_release); |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::RemoveRenderPassResource( |
| std::vector<RenderPassId> ids) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(!ids.empty()); |
| for (const auto& id : ids) { |
| auto it = offscreen_surfaces_.find(id); |
| DCHECK(it != offscreen_surfaces_.end()); |
| offscreen_surfaces_.erase(it); |
| } |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::CopyOutput( |
| RenderPassId id, |
| const gfx::Rect& copy_rect, |
| std::unique_ptr<CopyOutputRequest> request) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (!MakeCurrent()) { |
| request->SendResult( |
| std::make_unique<CopyOutputSkBitmapResult>(gfx::Rect(), SkBitmap())); |
| return; |
| } |
| |
| // TODO(crbug.com/644851): Complete the implementation for all request types, |
| // scaling, etc. |
| DCHECK_EQ(request->result_format(), CopyOutputResult::Format::RGBA_BITMAP); |
| DCHECK(!request->is_scaled()); |
| DCHECK(!request->has_result_selection() || |
| request->result_selection() == gfx::Rect(copy_rect.size())); |
| |
| DCHECK(!id || offscreen_surfaces_.find(id) != offscreen_surfaces_.end()); |
| auto* surface = id ? offscreen_surfaces_[id].get() : sk_surface_.get(); |
| |
| sk_sp<SkImage> copy_image = |
| surface->makeImageSnapshot()->makeSubset(RectToSkIRect(copy_rect)); |
| // Send copy request by copying into a bitmap. |
| SkBitmap bitmap; |
| copy_image->asLegacyBitmap(&bitmap); |
| request->SendResult( |
| std::make_unique<CopyOutputSkBitmapResult>(copy_rect, bitmap)); |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::FulfillPromiseTexture( |
| const ResourceMetadata& metadata, |
| std::unique_ptr<gpu::SharedImageRepresentationSkia>* shared_image_out, |
| GrBackendTexture* backend_texture) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(!*shared_image_out); |
| if (shared_image_representation_factory_->IsSharedImage(metadata.mailbox)) { |
| std::unique_ptr<gpu::SharedImageRepresentationSkia> shared_image = |
| shared_image_representation_factory_->ProduceSkia(metadata.mailbox); |
| DCHECK(shared_image); |
| if (!shared_image->BeginReadAccess(metadata.color_type, backend_texture)) { |
| DLOG(ERROR) |
| << "Failed to begin read access for SharedImageRepresentationSkia"; |
| return; |
| } |
| *shared_image_out = std::move(shared_image); |
| return; |
| } |
| |
| if (gpu_service_->is_using_vulkan()) { |
| // Probably this texture is created with wrong inteface (GLES2Interface). |
| DLOG(ERROR) << "Failed to fulfill the promise texture whose backend is not " |
| "compitable with vulkan."; |
| return; |
| } |
| |
| auto* mailbox_manager = gpu_service_->mailbox_manager(); |
| auto* texture_base = mailbox_manager->ConsumeTexture(metadata.mailbox); |
| if (!texture_base) { |
| DLOG(ERROR) << "Failed to fulfill the promise texture."; |
| return; |
| } |
| BindOrCopyTextureIfNecessary(texture_base); |
| gpu::GetGrBackendTexture(texture_base->target(), metadata.size, |
| *metadata.backend_format.getGLFormat(), |
| *metadata.driver_backend_format.getGLFormat(), |
| texture_base->service_id(), metadata.color_type, |
| backend_texture); |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::FulfillPromiseTexture( |
| const RenderPassId id, |
| std::unique_ptr<gpu::SharedImageRepresentationSkia>* shared_image_out, |
| GrBackendTexture* backend_texture) { |
| DCHECK(!*shared_image_out); |
| auto it = offscreen_surfaces_.find(id); |
| DCHECK(it != offscreen_surfaces_.end()); |
| sk_sp<SkSurface>& surface = it->second; |
| *backend_texture = |
| surface->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess); |
| DLOG_IF(ERROR, !backend_texture->isValid()) |
| << "Failed to fulfill the promise texture created from RenderPassId:" |
| << id; |
| } |
| |
| sk_sp<GrContextThreadSafeProxy> |
| SkiaOutputSurfaceImplOnGpu::GetGrContextThreadSafeProxy() { |
| return gr_context()->threadSafeProxy(); |
| } |
| |
| #if defined(OS_WIN) |
| void SkiaOutputSurfaceImplOnGpu::DidCreateAcceleratedSurfaceChildWindow( |
| gpu::SurfaceHandle parent_window, |
| gpu::SurfaceHandle child_window) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| NOTIMPLEMENTED(); |
| } |
| #endif |
| |
| void SkiaOutputSurfaceImplOnGpu::DidSwapBuffersComplete( |
| gpu::SwapBuffersCompleteParams params) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| params.swap_response.swap_id = pending_swap_completed_params_.front().first; |
| gfx::Size pixel_size = pending_swap_completed_params_.front().second; |
| pending_swap_completed_params_.pop_front(); |
| did_swap_buffer_complete_callback_.Run(params, pixel_size); |
| } |
| |
| const gpu::gles2::FeatureInfo* SkiaOutputSurfaceImplOnGpu::GetFeatureInfo() |
| const { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| return feature_info_.get(); |
| } |
| |
| const gpu::GpuPreferences& SkiaOutputSurfaceImplOnGpu::GetGpuPreferences() |
| const { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| NOTIMPLEMENTED(); |
| return gpu_preferences_; |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::BufferPresented( |
| const gfx::PresentationFeedback& feedback) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::AddFilter(IPC::MessageFilter* message_filter) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| NOTIMPLEMENTED(); |
| } |
| |
| int32_t SkiaOutputSurfaceImplOnGpu::GetRouteID() const { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| NOTIMPLEMENTED(); |
| return 0; |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::InitializeForGL() { |
| if (surface_handle_) { |
| gl_surface_ = gpu::ImageTransportSurface::CreateNativeSurface( |
| weak_ptr_factory_.GetWeakPtr(), surface_handle_, gl::GLSurfaceFormat()); |
| } else { |
| // surface_ could be null for pixel tests. |
| gl_surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(1, 1)); |
| } |
| DCHECK(gl_surface_); |
| |
| context_state_ = gpu_service_->GetContextStateForGLSurface(gl_surface_.get()); |
| if (!context_state_) { |
| LOG(FATAL) << "Failed to create GrContext"; |
| // TODO(penghuang): handle the failure. |
| } |
| |
| if (!MakeCurrent()) |
| return; |
| |
| auto* context = context_state_->real_context(); |
| gl_version_info_ = context->GetVersionInfo(); |
| |
| capabilities_.flipped_output_surface = gl_surface_->FlipsVertically(); |
| |
| // Get stencil bits from the default frame buffer. |
| auto* current_gl = context->GetCurrentGL(); |
| const auto* version = current_gl->Version; |
| auto* api = current_gl->Api; |
| api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, 0); |
| gr_context()->resetContext(kRenderTarget_GrGLBackendState); |
| GLint stencil_bits = 0; |
| if (version->is_desktop_core_profile) { |
| api->glGetFramebufferAttachmentParameterivEXTFn( |
| GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, |
| &stencil_bits); |
| } else { |
| api->glGetIntegervFn(GL_STENCIL_BITS, &stencil_bits); |
| } |
| CHECK_GL_ERROR(); |
| capabilities_.supports_stencil = stencil_bits > 0; |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::InitializeForVulkan() { |
| context_state_ = gpu_service_->GetContextStateForVulkan(); |
| DCHECK(context_state_); |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::BindOrCopyTextureIfNecessary( |
| gpu::TextureBase* texture_base) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (texture_base->GetType() != gpu::TextureBase::Type::kValidated) |
| return; |
| // If a texture is validated and bound to an image, we may defer copying the |
| // image to the texture until the texture is used. It is for implementing low |
| // latency drawing (e.g. fast ink) and avoiding unnecessary texture copy. So |
| // we need check the texture image state, and bind or copy the image to the |
| // texture if necessary. |
| auto* texture = gpu::gles2::Texture::CheckedCast(texture_base); |
| gpu::gles2::Texture::ImageState image_state; |
| auto* image = texture->GetLevelImage(GL_TEXTURE_2D, 0, &image_state); |
| if (image && image_state == gpu::gles2::Texture::UNBOUND) { |
| glBindTexture(texture_base->target(), texture_base->service_id()); |
| if (image->BindTexImage(texture_base->target())) { |
| } else { |
| texture->SetLevelImageState(texture_base->target(), 0, |
| gpu::gles2::Texture::COPIED); |
| if (!image->CopyTexImage(texture_base->target())) |
| LOG(ERROR) << "Failed to copy a gl image to texture."; |
| } |
| } |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::OnSwapBuffers() { |
| uint64_t swap_id = swap_id_++; |
| gfx::Size pixel_size(sk_surface_->width(), sk_surface_->height()); |
| pending_swap_completed_params_.emplace_back(swap_id, pixel_size); |
| } |
| |
| void SkiaOutputSurfaceImplOnGpu::CreateSkSurfaceForVulkan() { |
| #if BUILDFLAG(ENABLE_VULKAN) |
| auto* swap_chain = vulkan_surface_->GetSwapChain(); |
| auto index = swap_chain->current_image(); |
| auto& sk_surface = sk_surfaces_[index]; |
| if (!sk_surface) { |
| SkSurfaceProps surface_props = |
| SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType); |
| VkImage vk_image = swap_chain->GetCurrentImage(); |
| VkImageLayout vk_image_layout = swap_chain->GetCurrentImageLayout(); |
| GrVkImageInfo vk_image_info; |
| vk_image_info.fImage = vk_image; |
| vk_image_info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0}; |
| vk_image_info.fImageLayout = vk_image_layout; |
| vk_image_info.fImageTiling = VK_IMAGE_TILING_OPTIMAL; |
| vk_image_info.fFormat = VK_FORMAT_B8G8R8A8_UNORM; |
| vk_image_info.fLevelCount = 1; |
| GrBackendRenderTarget render_target(vulkan_surface_->size().width(), |
| vulkan_surface_->size().height(), 0, 0, |
| vk_image_info); |
| sk_surface = SkSurface::MakeFromBackendRenderTarget( |
| gr_context(), render_target, kTopLeft_GrSurfaceOrigin, |
| kBGRA_8888_SkColorType, nullptr, &surface_props); |
| } else { |
| auto backend = sk_surface->getBackendRenderTarget( |
| SkSurface::kFlushRead_BackendHandleAccess); |
| backend.setVkImageLayout(swap_chain->GetCurrentImageLayout()); |
| } |
| |
| sk_surface_ = sk_surface; |
| #endif |
| } |
| |
| bool SkiaOutputSurfaceImplOnGpu::MakeCurrent() { |
| if (!gpu_service_->is_using_vulkan()) { |
| if (!context_state_->MakeCurrent(gl_surface_.get())) { |
| LOG(ERROR) << "Failed to make current."; |
| context_lost_callback_.Run(); |
| return false; |
| } |
| context_state_->need_context_state_reset = true; |
| } |
| return true; |
| } |
| |
| } // namespace viz |