/* * Copyright 2021 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/graphite/DrawContext.h" #include "include/core/SkColorSpace.h" #include "include/core/SkPixmap.h" #include "include/private/SkColorData.h" #include "include/gpu/graphite/Context.h" #include "include/gpu/graphite/Recorder.h" #include "src/gpu/graphite/Buffer.h" #include "src/gpu/graphite/Caps.h" #include "src/gpu/graphite/CommandBuffer.h" #include "src/gpu/graphite/ContextPriv.h" #include "src/gpu/graphite/DrawList.h" #include "src/gpu/graphite/DrawPass.h" #include "src/gpu/graphite/RecorderPriv.h" #include "src/gpu/graphite/RenderPassTask.h" #include "src/gpu/graphite/ResourceTypes.h" #include "src/gpu/graphite/SharedContext.h" #include "src/gpu/graphite/TextureProxy.h" #include "src/gpu/graphite/TextureProxyView.h" #include "src/gpu/graphite/UploadTask.h" #include "src/gpu/graphite/geom/BoundsManager.h" #include "src/gpu/graphite/geom/Geometry.h" #include "src/gpu/graphite/text/AtlasManager.h" #ifdef SK_ENABLE_PIET_GPU #include "src/gpu/graphite/PietRenderTask.h" #endif namespace skgpu::graphite { sk_sp DrawContext::Make(sk_sp target, SkISize deviceSize, const SkColorInfo& colorInfo, const SkSurfaceProps& props) { if (!target) { return nullptr; } // TODO: validate that the color type and alpha type are compatible with the target's info SkASSERT(!target->isInstantiated() || target->dimensions() == deviceSize); SkImageInfo imageInfo = SkImageInfo::Make(deviceSize, colorInfo); return sk_sp(new DrawContext(std::move(target), imageInfo, props)); } DrawContext::DrawContext(sk_sp target, const SkImageInfo& ii, const SkSurfaceProps& props) : fTarget(std::move(target)) , fImageInfo(ii) , fSurfaceProps(props) , fPendingDraws(std::make_unique()) , fPendingUploads(std::make_unique()) { // TBD - Will probably want DrawLists (and its internal commands) to come from an arena // that the DC manages. } DrawContext::~DrawContext() { // If the DC is destroyed and there are pending commands, they won't be drawn. fPendingDraws.reset(); fDrawPasses.clear(); } TextureProxyView DrawContext::readSurfaceView(const Caps* caps) { TextureProxy* proxy = this->target(); if (!caps->isTexturable(proxy->textureInfo())) { return {}; } Swizzle swizzle = caps->getReadSwizzle(this->imageInfo().colorType(), proxy->textureInfo()); return TextureProxyView(sk_ref_sp(proxy), swizzle); } void DrawContext::clear(const SkColor4f& clearColor) { fPendingLoadOp = LoadOp::kClear; SkPMColor4f pmColor = clearColor.premul(); fPendingClearColor = pmColor.array(); // a fullscreen clear will overwrite anything that came before, so start a new DrawList // and clear any drawpasses that haven't been snapped yet fPendingDraws = std::make_unique(); fDrawPasses.clear(); } void DrawContext::recordDraw(const Renderer* renderer, const Transform& localToDevice, const Geometry& geometry, const Clip& clip, DrawOrder ordering, const PaintParams* paint, const StrokeStyle* stroke) { SkASSERT(SkIRect::MakeSize(this->imageInfo().dimensions()).contains(clip.scissor())); fPendingDraws->recordDraw(renderer, localToDevice, geometry, clip, ordering, paint, stroke); } bool DrawContext::recordTextUploads(AtlasManager* am) { return am->recordUploads(fPendingUploads.get(), /*useCachedUploads=*/false); } bool DrawContext::recordUpload(Recorder* recorder, sk_sp targetProxy, const SkColorInfo& srcColorInfo, const SkColorInfo& dstColorInfo, const std::vector& levels, const SkIRect& dstRect, std::unique_ptr condContext) { // Our caller should have clipped to the bounds of the surface already. SkASSERT(targetProxy->isFullyLazy() || SkIRect::MakeSize(targetProxy->dimensions()).contains(dstRect)); return fPendingUploads->recordUpload(recorder, std::move(targetProxy), srcColorInfo, dstColorInfo, levels, dstRect, std::move(condContext)); } #ifdef SK_ENABLE_PIET_GPU bool DrawContext::recordPietSceneRender(Recorder*, sk_sp targetProxy, sk_sp scene) { fPendingPietRenders.push_back(PietRenderInstance(std::move(scene), std::move(targetProxy))); return true; } #endif void DrawContext::snapDrawPass(Recorder* recorder) { if (fPendingDraws->drawCount() == 0 && fPendingLoadOp != LoadOp::kClear) { return; } auto pass = DrawPass::Make(recorder, std::move(fPendingDraws), fTarget, this->imageInfo(), std::make_pair(fPendingLoadOp, fPendingStoreOp), fPendingClearColor); fDrawPasses.push_back(std::move(pass)); fPendingDraws = std::make_unique(); fPendingLoadOp = LoadOp::kLoad; fPendingStoreOp = StoreOp::kStore; } RenderPassDesc RenderPassDesc::Make(const Caps* caps, const TextureInfo& targetInfo, LoadOp loadOp, StoreOp storeOp, SkEnumBitMask depthStencilFlags, const std::array& clearColor, bool requiresMSAA) { RenderPassDesc desc; // It doesn't make sense to have a storeOp for our main target not be store. Why are we doing // this DrawPass then SkASSERT(storeOp == StoreOp::kStore); if (requiresMSAA) { // TODO: If the resolve texture isn't readable, the MSAA color attachment will need to be // persistently associated with the framebuffer, in which case it's not discardable. desc.fColorAttachment.fTextureInfo = caps->getDefaultMSAATextureInfo(targetInfo, Discardable::kYes); if (loadOp != LoadOp::kClear) { desc.fColorAttachment.fLoadOp = LoadOp::kDiscard; } else { desc.fColorAttachment.fLoadOp = LoadOp::kClear; } desc.fColorAttachment.fStoreOp = StoreOp::kDiscard; desc.fColorResolveAttachment.fTextureInfo = targetInfo; if (loadOp != LoadOp::kLoad) { desc.fColorResolveAttachment.fLoadOp = LoadOp::kDiscard; } else { desc.fColorResolveAttachment.fLoadOp = LoadOp::kLoad; } desc.fColorResolveAttachment.fStoreOp = storeOp; } else { desc.fColorAttachment.fTextureInfo = targetInfo; desc.fColorAttachment.fLoadOp = loadOp; desc.fColorAttachment.fStoreOp = storeOp; } desc.fClearColor = clearColor; if (depthStencilFlags != DepthStencilFlags::kNone) { desc.fDepthStencilAttachment.fTextureInfo = caps->getDefaultDepthStencilTextureInfo( depthStencilFlags, desc.fColorAttachment.fTextureInfo.numSamples(), Protected::kNo); // Always clear the depth and stencil to 0 at the start of a DrawPass, but discard at the // end since their contents do not affect the next frame. desc.fDepthStencilAttachment.fLoadOp = LoadOp::kClear; desc.fClearDepth = 0.f; desc.fClearStencil = 0; desc.fDepthStencilAttachment.fStoreOp = StoreOp::kDiscard; } return desc; } sk_sp DrawContext::snapRenderPassTask(Recorder* recorder) { this->snapDrawPass(recorder); if (fDrawPasses.empty()) { return nullptr; } const Caps* caps = recorder->priv().caps(); // TODO: At this point we would determine all the targets used by the drawPasses, // build up the union of them and store them in the RenderPassDesc. However, for // the moment we should have only one drawPass. SkASSERT(fDrawPasses.size() == 1); auto& drawPass = fDrawPasses[0]; const TextureInfo& targetInfo = drawPass->target()->textureInfo(); auto [loadOp, storeOp] = drawPass->ops(); RenderPassDesc desc = RenderPassDesc::Make(caps, targetInfo, loadOp, storeOp, drawPass->depthStencilFlags(), drawPass->clearColor(), drawPass->requiresMSAA()); sk_sp targetProxy = sk_ref_sp(fDrawPasses[0]->target()); return RenderPassTask::Make(std::move(fDrawPasses), desc, std::move(targetProxy)); } sk_sp DrawContext::snapUploadTask(Recorder* recorder) { if (!fPendingUploads || fPendingUploads->size() == 0) { return nullptr; } sk_sp uploadTask = UploadTask::Make(fPendingUploads.get()); fPendingUploads = std::make_unique(); return uploadTask; } #ifdef SK_ENABLE_PIET_GPU sk_sp DrawContext::snapPietRenderTask(Recorder* recorder) { if (fPendingPietRenders.empty()) { return nullptr; } return sk_sp(new PietRenderTask(std::move(fPendingPietRenders))); } #endif } // namespace skgpu::graphite