// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/modules/webgl/webgl_multi_draw.h"

#include "gpu/command_buffer/client/gles2_interface.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"

namespace blink {

WebGLMultiDraw::WebGLMultiDraw(WebGLRenderingContextBase* context,
                               ExecutionContext* execution_context)
    : WebGLExtension(context) {
  UseCounter::CountWebDXFeature(execution_context,
                                WebDXFeature::kWebglMultiDraw);
  context->ExtensionsUtil()->EnsureExtensionEnabled("GL_WEBGL_multi_draw");
  context->ExtensionsUtil()->EnsureExtensionEnabled("GL_ANGLE_multi_draw");

  // Spec requires ANGLE_instanced_arrays to be implicitly turned on
  // here in WebGL 1.0 contexts.
  if (!context->IsWebGL2()) {
    context->EnableExtensionIfSupported("ANGLE_instanced_arrays",
                                        execution_context);
  }
}

WebGLExtensionName WebGLMultiDraw::GetName() const {
  return kWebGLMultiDrawName;
}

bool WebGLMultiDraw::Supported(WebGLRenderingContextBase* context) {
  return context->ExtensionsUtil()->SupportsExtension("GL_WEBGL_multi_draw") ||
         (context->ExtensionsUtil()->SupportsExtension("GL_ANGLE_multi_draw") &&
          context->ExtensionsUtil()->SupportsExtension(
              "GL_ANGLE_instanced_arrays"));
}

const char* WebGLMultiDraw::ExtensionName() {
  return "WEBGL_multi_draw";
}

void WebGLMultiDraw::multiDrawArraysWEBGL(GLenum mode,
                                          base::span<const int32_t> firsts,
                                          GLuint firstsOffset,
                                          base::span<const int32_t> counts,
                                          GLuint countsOffset,
                                          GLsizei drawcount) {
  WebGLExtensionScopedContext scoped(this);
  if (scoped.IsLost() ||
      !ValidateDrawcount(&scoped, "multiDrawArraysWEBGL", drawcount) ||
      !ValidateArray(&scoped, "multiDrawArraysWEBGL",
                     "firstsOffset out of bounds", firsts.size(), firstsOffset,
                     drawcount) ||
      !ValidateArray(&scoped, "multiDrawArraysWEBGL",
                     "countsOffset out of bounds", counts.size(), countsOffset,
                     drawcount)) {
    return;
  }

  scoped.Context()->DrawWrapper(
      "multiDrawArraysWEBGL", CanvasPerformanceMonitor::DrawType::kDrawArrays,
      [&]() {
        scoped.Context()->ContextGL()->MultiDrawArraysWEBGL(
            mode, &firsts[firstsOffset], &counts[countsOffset], drawcount);
      });
}

void WebGLMultiDraw::multiDrawElementsWEBGL(GLenum mode,
                                            base::span<const int32_t> counts,
                                            GLuint countsOffset,
                                            GLenum type,
                                            base::span<const int32_t> offsets,
                                            GLuint offsetsOffset,
                                            GLsizei drawcount) {
  WebGLExtensionScopedContext scoped(this);
  if (scoped.IsLost() ||
      !ValidateDrawcount(&scoped, "multiDrawElementsWEBGL", drawcount) ||
      !ValidateArray(&scoped, "multiDrawElementsWEBGL",
                     "countsOffset out of bounds", counts.size(), countsOffset,
                     drawcount) ||
      !ValidateArray(&scoped, "multiDrawElementsWEBGL",
                     "offsetsOffset out of bounds", offsets.size(),
                     offsetsOffset, drawcount)) {
    return;
  }

  scoped.Context()->DrawWrapper(
      "multiDrawElementsWEBGL",
      CanvasPerformanceMonitor::DrawType::kDrawElements, [&]() {
        scoped.Context()->ContextGL()->MultiDrawElementsWEBGL(
            mode, &counts[countsOffset], type, &offsets[offsetsOffset],
            drawcount);
      });
}

void WebGLMultiDraw::multiDrawArraysInstancedWEBGL(
    GLenum mode,
    base::span<const int32_t> firsts,
    GLuint firstsOffset,
    base::span<const int32_t> counts,
    GLuint countsOffset,
    base::span<const int32_t> instanceCounts,
    GLuint instanceCountsOffset,
    GLsizei drawcount) {
  WebGLExtensionScopedContext scoped(this);
  if (scoped.IsLost() ||
      !ValidateDrawcount(&scoped, "multiDrawArraysInstancedWEBGL", drawcount) ||
      !ValidateArray(&scoped, "multiDrawArraysInstancedWEBGL",
                     "firstsOffset out of bounds", firsts.size(), firstsOffset,
                     drawcount) ||
      !ValidateArray(&scoped, "multiDrawArraysInstancedWEBGL",
                     "countsOffset out of bounds", counts.size(), countsOffset,
                     drawcount) ||
      !ValidateArray(&scoped, "multiDrawArraysInstancedWEBGL",
                     "instanceCountsOffset out of bounds",
                     instanceCounts.size(), instanceCountsOffset, drawcount)) {
    return;
  }

  scoped.Context()->DrawWrapper(
      "multiDrawArraysInstancedWEBGL",
      CanvasPerformanceMonitor::DrawType::kDrawArrays, [&]() {
        scoped.Context()->ContextGL()->MultiDrawArraysInstancedWEBGL(
            mode, &firsts[firstsOffset], &counts[countsOffset],
            &instanceCounts[instanceCountsOffset], drawcount);
      });
}

void WebGLMultiDraw::multiDrawElementsInstancedWEBGL(
    GLenum mode,
    base::span<const int32_t> counts,
    GLuint countsOffset,
    GLenum type,
    base::span<const int32_t> offsets,
    GLuint offsetsOffset,
    base::span<const int32_t> instanceCounts,
    GLuint instanceCountsOffset,
    GLsizei drawcount) {
  WebGLExtensionScopedContext scoped(this);
  if (scoped.IsLost() ||
      !ValidateDrawcount(&scoped, "multiDrawElementsInstancedWEBGL",
                         drawcount) ||
      !ValidateArray(&scoped, "multiDrawElementsInstancedWEBGL",
                     "countsOffset out of bounds", counts.size(), countsOffset,
                     drawcount) ||
      !ValidateArray(&scoped, "multiDrawElementsInstancedWEBGL",
                     "offsetsOffset out of bounds", offsets.size(),
                     offsetsOffset, drawcount) ||
      !ValidateArray(&scoped, "multiDrawElementsInstancedWEBGL",
                     "instanceCountsOffset out of bounds",
                     instanceCounts.size(), instanceCountsOffset, drawcount)) {
    return;
  }

  scoped.Context()->DrawWrapper(
      "multiDrawElementsInstancedWEBGL",
      CanvasPerformanceMonitor::DrawType::kDrawElements, [&]() {
        scoped.Context()->ContextGL()->MultiDrawElementsInstancedWEBGL(
            mode, &counts[countsOffset], type, &offsets[offsetsOffset],
            &instanceCounts[instanceCountsOffset], drawcount);
      });
}

}  // namespace blink
