text: /*
   * Copyright 2017 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  #include "src/gpu/mock/GrMockBuffer.h"
  #include "src/gpu/mock/GrMockCaps.h"
  #include "src/gpu/mock/GrMockGpu.h"
  #include "src/gpu/mock/GrMockGpuCommandBuffer.h"
  #include "src/gpu/mock/GrMockStencilAttachment.h"
  #include "src/gpu/mock/GrMockTexture.h"
  #include <atomic>
  
  int GrMockGpu::NextInternalTextureID() {
      static std::atomic<int> nextID{1};
      int id;
      do {
          id = nextID.fetch_add(1);
      } while (0 == id);  // Reserve 0 for an invalid ID.
      return id;
  }
  
  int GrMockGpu::NextExternalTextureID() {
      // We use negative ints for the "testing only external textures" so they can easily be
      // identified when debugging.
      static std::atomic<int> nextID{-1};
      return nextID--;
  }
  
  int GrMockGpu::NextInternalRenderTargetID() {
      // We start off with large numbers to differentiate from texture IDs, even though they're
      // technically in a different space.
      static std::atomic<int> nextID{SK_MaxS32};
      return nextID--;
  }
  
  int GrMockGpu::NextExternalRenderTargetID() {
      // We use large negative ints for the "testing only external render targets" so they can easily
      // be identified when debugging.
      static std::atomic<int> nextID{SK_MinS32};
      return nextID++;
  }
  
  sk_sp<GrGpu> GrMockGpu::Make(const GrMockOptions* mockOptions,
                               const GrContextOptions& contextOptions, GrContext* context) {
      static const GrMockOptions kDefaultOptions = GrMockOptions();
      if (!mockOptions) {
          mockOptions = &kDefaultOptions;
      }
      return sk_sp<GrGpu>(new GrMockGpu(context, *mockOptions, contextOptions));
  }
  
  GrGpuRTCommandBuffer* GrMockGpu::getCommandBuffer(
                                  GrRenderTarget* rt, GrSurfaceOrigin origin, const SkRect& bounds,
                                  const GrGpuRTCommandBuffer::LoadAndStoreInfo&,
                                  const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo&) {
      return new GrMockGpuRTCommandBuffer(this, rt, origin);
  }
  
  GrGpuTextureCommandBuffer* GrMockGpu::getCommandBuffer(GrTexture* texture, GrSurfaceOrigin origin) {
      return new GrMockGpuTextureCommandBuffer(texture, origin);
  }
  
  void GrMockGpu::submit(GrGpuCommandBuffer* buffer) {
      if (buffer->asRTCommandBuffer()) {
          this->submitCommandBuffer(
                          static_cast<GrMockGpuRTCommandBuffer*>(buffer->asRTCommandBuffer()));
      }
  
      delete buffer;
  }
  
  void GrMockGpu::submitCommandBuffer(const GrMockGpuRTCommandBuffer* cmdBuffer) {
      for (int i = 0; i < cmdBuffer->numDraws(); ++i) {
          fStats.incNumDraws();
      }
  }
  
  GrMockGpu::GrMockGpu(GrContext* context, const GrMockOptions& options,
                       const GrContextOptions& contextOptions)
          : INHERITED(context)
          , fMockOptions(options) {
      fCaps.reset(new GrMockCaps(contextOptions, options));
  }
  
  void GrMockGpu::querySampleLocations(GrRenderTarget* rt, SkTArray<SkPoint>* sampleLocations) {
      sampleLocations->reset();
      int numRemainingSamples = rt->numSamples();
      while (numRemainingSamples > 0) {
          // Use standard D3D sample locations.
          switch (numRemainingSamples) {
              case 0:
              case 1:
                  sampleLocations->push_back().set(.5, .5);
                  break;
              case 2:
                  sampleLocations->push_back().set(.75, .75);
                  sampleLocations->push_back().set(.25, .25);
                  break;
              case 3:
              case 4:
                  sampleLocations->push_back().set(.375, .125);
                  sampleLocations->push_back().set(.875, .375);
                  sampleLocations->push_back().set(.125, .625);
                  sampleLocations->push_back().set(.625, .875);
                  break;
              case 5:
              case 6:
              case 7:
              case 8:
                  sampleLocations->push_back().set(.5625, .3125);
                  sampleLocations->push_back().set(.4375, .6875);
                  sampleLocations->push_back().set(.8125, .5625);
                  sampleLocations->push_back().set(.3125, .1875);
                  sampleLocations->push_back().set(.1875, .8125);
                  sampleLocations->push_back().set(.0625, .4375);
                  sampleLocations->push_back().set(.6875, .4375);
                  sampleLocations->push_back().set(.4375, .0625);
                  break;
              default:
                  sampleLocations->push_back().set(.5625, .5625);
                  sampleLocations->push_back().set(.4375, .3125);
                  sampleLocations->push_back().set(.3125, .6250);
                  sampleLocations->push_back().set(.2500, .4375);
                  sampleLocations->push_back().set(.1875, .3750);
                  sampleLocations->push_back().set(.6250, .8125);
                  sampleLocations->push_back().set(.8125, .6875);
                  sampleLocations->push_back().set(.6875, .1875);
                  sampleLocations->push_back().set(.3750, .8750);
                  sampleLocations->push_back().set(.5000, .0625);
                  sampleLocations->push_back().set(.2500, .1250);
                  sampleLocations->push_back().set(.1250, .2500);
                  sampleLocations->push_back().set(.0000, .5000);
                  sampleLocations->push_back().set(.4375, .2500);
                  sampleLocations->push_back().set(.8750, .4375);
                  sampleLocations->push_back().set(.0625, .0000);
                  break;
          }
          numRemainingSamples = rt->numSamples() - sampleLocations->count();
      }
  }
  
  sk_sp<GrTexture> GrMockGpu::onCreateTexture(const GrSurfaceDesc& desc, GrRenderable renderable,
                                              int renderTargetSampleCnt, SkBudgeted budgeted,
                                              GrProtected isProtected, const GrMipLevel texels[],
                                              int mipLevelCount) {
      if (fMockOptions.fFailTextureAllocations) {
          return nullptr;
      }
  
      GrColorType ct = GrPixelConfigToColorType(desc.fConfig);
      if (GrColorType::kUnknown == ct) {
          return nullptr;
      }
  
      GrMipMapsStatus mipMapsStatus = mipLevelCount > 1 ? GrMipMapsStatus::kValid
                                                        : GrMipMapsStatus::kNotAllocated;
      GrMockTextureInfo texInfo(ct, NextInternalTextureID());
      if (renderable == GrRenderable::kYes) {
          GrMockRenderTargetInfo rtInfo(ct, NextInternalRenderTargetID());
          return sk_sp<GrTexture>(new GrMockTextureRenderTarget(this, budgeted, desc,
                                                                renderTargetSampleCnt, isProtected,
                                                                mipMapsStatus, texInfo, rtInfo));
      }
      return sk_sp<GrTexture>(
              new GrMockTexture(this, budgeted, desc, isProtected, mipMapsStatus, texInfo));
  }
  
  sk_sp<GrTexture> GrMockGpu::onCreateCompressedTexture(int width, int height,
                                                        SkImage::CompressionType compressionType,
                                                        SkBudgeted budgeted, const void* data) {
      return nullptr;
  }
  
  sk_sp<GrTexture> GrMockGpu::onWrapBackendTexture(const GrBackendTexture& tex, GrColorType colorType,
                                                   GrWrapOwnership ownership,
                                                   GrWrapCacheable wrapType, GrIOType ioType) {
      GrMockTextureInfo texInfo;
      SkAssertResult(tex.getMockTextureInfo(&texInfo));
  
      SkASSERT(colorType == texInfo.fColorType);
      GrSurfaceDesc desc;
      desc.fWidth = tex.width();
      desc.fHeight = tex.height();
      desc.fConfig = texInfo.pixelConfig();
  
      GrMipMapsStatus mipMapsStatus = tex.hasMipMaps() ? GrMipMapsStatus::kValid
                                                       : GrMipMapsStatus::kNotAllocated;
      auto isProtected = GrProtected(tex.isProtected());
      return sk_sp<GrTexture>(
              new GrMockTexture(this, desc, isProtected, mipMapsStatus, texInfo, wrapType, ioType));
  }
  
  sk_sp<GrTexture> GrMockGpu::onWrapRenderableBackendTexture(const GrBackendTexture& tex,
                                                             int sampleCnt,
                                                             GrColorType colorType,
                                                             GrWrapOwnership ownership,
                                                             GrWrapCacheable cacheable) {
      GrMockTextureInfo texInfo;
      SkAssertResult(tex.getMockTextureInfo(&texInfo));
  
      SkASSERT(colorType == texInfo.fColorType);
      GrSurfaceDesc desc;
      desc.fWidth = tex.width();
      desc.fHeight = tex.height();
      desc.fConfig = texInfo.pixelConfig();
  
      GrMipMapsStatus mipMapsStatus =
              tex.hasMipMaps() ? GrMipMapsStatus::kValid : GrMipMapsStatus::kNotAllocated;
  
      // The client gave us the texture ID but we supply the render target ID.
      GrMockRenderTargetInfo rtInfo(texInfo.fColorType, NextInternalRenderTargetID());
  
      auto isProtected = GrProtected(tex.isProtected());
      return sk_sp<GrTexture>(new GrMockTextureRenderTarget(
              this, desc, sampleCnt, isProtected, mipMapsStatus, texInfo, rtInfo, cacheable));
  }
  
  sk_sp<GrRenderTarget> GrMockGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& rt,
                                                             GrColorType colorType) {
      GrMockRenderTargetInfo info;
      SkAssertResult(rt.getMockRenderTargetInfo(&info));
  
      SkASSERT(colorType == info.colorType());
      GrSurfaceDesc desc;
      desc.fWidth = rt.width();
      desc.fHeight = rt.height();
      desc.fConfig = info.pixelConfig();
  
      auto isProtected = GrProtected(rt.isProtected());
      return sk_sp<GrRenderTarget>(new GrMockRenderTarget(this, GrMockRenderTarget::kWrapped, desc,
                                                          rt.sampleCnt(), isProtected, info));
  }
  
  sk_sp<GrRenderTarget> GrMockGpu::onWrapBackendTextureAsRenderTarget(const GrBackendTexture& tex,
                                                                      int sampleCnt,
                                                                      GrColorType colorType) {
      GrMockTextureInfo texInfo;
      SkAssertResult(tex.getMockTextureInfo(&texInfo));
  
      SkASSERT(colorType == texInfo.fColorType);
      GrSurfaceDesc desc;
      desc.fWidth = tex.width();
      desc.fHeight = tex.height();
      desc.fConfig = texInfo.pixelConfig();
  
      // The client gave us the texture ID but we supply the render target ID.
      GrMockRenderTargetInfo rtInfo(texInfo.fColorType, NextInternalRenderTargetID());
  
      auto isProtected = GrProtected(tex.isProtected());
      return sk_sp<GrRenderTarget>(new GrMockRenderTarget(this, GrMockRenderTarget::kWrapped, desc,
                                                          sampleCnt, isProtected, rtInfo));
  }
  
  sk_sp<GrGpuBuffer> GrMockGpu::onCreateBuffer(size_t sizeInBytes, GrGpuBufferType type,
                                               GrAccessPattern accessPattern, const void*) {
      return sk_sp<GrGpuBuffer>(new GrMockBuffer(this, sizeInBytes, type, accessPattern));
  }
  
  GrStencilAttachment* GrMockGpu::createStencilAttachmentForRenderTarget(
          const GrRenderTarget* rt, int width, int height, int numStencilSamples) {
      SkASSERT(numStencilSamples == rt->numSamples());
      static constexpr int kBits = 8;
      fStats.incStencilAttachmentCreates();
      return new GrMockStencilAttachment(this, width, height, kBits, rt->numSamples());
  }
  
  GrBackendTexture GrMockGpu::createBackendTexture(int w, int h,
                                                   const GrBackendFormat& format,
                                                   GrMipMapped mipMapped,
                                                   GrRenderable /* renderable */,
                                                   const void* /* pixels */,
                                                   size_t /* rowBytes */,
                                                   const SkColor4f* /* color */,
                                                   GrProtected /* isProtected */) {
  
      if (!format.getMockColorType()) {
          return GrBackendTexture();  // invalid;
      }
  
      if (!this->caps()->isFormatTexturable(*format.getMockColorType(), format)) {
          return GrBackendTexture();  // invalid
      }
  
      GrMockTextureInfo info(*format.getMockColorType(), NextExternalTextureID());
  
      fOutstandingTestingOnlyTextureIDs.add(info.fID);
      return GrBackendTexture(w, h, mipMapped, info);
  }
  
  void GrMockGpu::deleteBackendTexture(const GrBackendTexture& tex) {
      SkASSERT(GrBackendApi::kMock == tex.backend());
  
      GrMockTextureInfo info;
      if (tex.getMockTextureInfo(&info)) {
          fOutstandingTestingOnlyTextureIDs.remove(info.fID);
      }
  }
  
  #if GR_TEST_UTILS
  bool GrMockGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
      SkASSERT(GrBackendApi::kMock == tex.backend());
  
      GrMockTextureInfo info;
      if (!tex.getMockTextureInfo(&info)) {
          return false;
      }
  
      return fOutstandingTestingOnlyTextureIDs.contains(info.fID);
  }
  
  GrBackendRenderTarget GrMockGpu::createTestingOnlyBackendRenderTarget(int w, int h,
                                                                        GrColorType colorType) {
      GrMockRenderTargetInfo info(colorType, NextExternalRenderTargetID());
      static constexpr int kSampleCnt = 1;
      static constexpr int kStencilBits = 8;
      return GrBackendRenderTarget(w, h, kSampleCnt, kStencilBits, info);
  }
  
  void GrMockGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget&) {}
  #endif
  