aw_gl_surface_external_stencil.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "android_webview/browser/gfx/aw_gl_surface_external_stencil.h"
  5. #include "android_webview/browser/gfx/scoped_app_gl_state_restore.h"
  6. #include "base/strings/stringize_macros.h"
  7. #include "ui/gfx/geometry/quad_f.h"
  8. #include "ui/gl/gl_bindings.h"
  9. #include "ui/gl/gl_helper.h"
  10. namespace android_webview {
  11. class AwGLSurfaceExternalStencil::BlitContext {
  12. public:
  13. BlitContext() {
  14. // NOTE: Quad is flipped vertically.
  15. // clang-format off
  16. const GLchar vertex_shader_str[] =
  17. STRINGIZE(
  18. attribute vec2 position;
  19. attribute vec2 texcoords;
  20. varying vec2 v_texcoords;
  21. void main()
  22. {
  23. v_texcoords = vec2(texcoords.x, 1.0 - texcoords.y);
  24. vec2 pos = position * 2.0 - vec2(1.0);
  25. gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);
  26. }
  27. );
  28. const GLchar fragment_shader_str[] =
  29. STRINGIZE(
  30. precision mediump float;
  31. varying vec2 v_texcoords;
  32. uniform sampler2D texture;
  33. void main()
  34. {
  35. gl_FragColor = texture2D ( texture, v_texcoords );
  36. }
  37. );
  38. // clang-format on
  39. GLuint vertex_shader =
  40. gl::GLHelper::LoadShader(GL_VERTEX_SHADER, vertex_shader_str);
  41. GLuint fragment_shader =
  42. gl::GLHelper::LoadShader(GL_FRAGMENT_SHADER, fragment_shader_str);
  43. program_ = glCreateProgram();
  44. glAttachShader(program_, vertex_shader);
  45. glAttachShader(program_, fragment_shader);
  46. glBindAttribLocation(program_, 0, "position");
  47. glBindAttribLocation(program_, 1, "texcoords");
  48. glLinkProgram(program_);
  49. GLint linked;
  50. glGetProgramiv(program_, GL_LINK_STATUS, &linked);
  51. DCHECK(linked);
  52. glDeleteShader(vertex_shader);
  53. glDeleteShader(fragment_shader);
  54. texture_uniform_ = glGetUniformLocation(program_, "texture");
  55. glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_max_vertex_attribs_);
  56. }
  57. ~BlitContext() { glDeleteProgram(program_); }
  58. void Bind() {
  59. // If vertex array objects are supported we need to reset it to default one,
  60. // so we won't break someones else VAS by changing attributes.
  61. if (gl::g_current_gl_driver->fn.glBindVertexArrayOESFn) {
  62. glBindVertexArrayOES(0);
  63. }
  64. glUseProgram(program_);
  65. for (GLint i = 2; i < gl_max_vertex_attribs_; ++i) {
  66. glDisableVertexAttribArray(i);
  67. }
  68. // Note that function is not ANGLE only.
  69. if (gl::g_current_gl_driver->fn.glVertexAttribDivisorANGLEFn) {
  70. glVertexAttribDivisorANGLE(0, 0);
  71. glVertexAttribDivisorANGLE(1, 0);
  72. }
  73. glEnableVertexAttribArray(0);
  74. glEnableVertexAttribArray(1);
  75. glBindBuffer(GL_ARRAY_BUFFER, 0);
  76. glUniform1i(texture_uniform_, 0);
  77. }
  78. private:
  79. GLuint program_;
  80. GLint texture_uniform_;
  81. GLint gl_max_vertex_attribs_;
  82. };
  83. class AwGLSurfaceExternalStencil::FrameBuffer {
  84. public:
  85. FrameBuffer(gfx::Size size) : size_(size) {
  86. glGenTextures(1, &texture_id_);
  87. glBindTexture(GL_TEXTURE_2D, texture_id_);
  88. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
  89. GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
  90. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  91. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  92. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  93. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  94. glBindTexture(GL_TEXTURE_2D, 0);
  95. glGenFramebuffersEXT(1, &frame_buffer_object_);
  96. glBindFramebufferEXT(GL_FRAMEBUFFER, frame_buffer_object_);
  97. glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
  98. GL_TEXTURE_2D, texture_id_, 0);
  99. DCHECK(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) ==
  100. GL_FRAMEBUFFER_COMPLETE)
  101. << "Failed to set up framebuffer for WebView GL drawing.";
  102. }
  103. void Clear() {
  104. glBindFramebufferEXT(GL_FRAMEBUFFER, frame_buffer_object_);
  105. glDisable(GL_SCISSOR_TEST);
  106. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  107. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  108. glClear(GL_COLOR_BUFFER_BIT);
  109. }
  110. ~FrameBuffer() {
  111. glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
  112. glBindTexture(GL_TEXTURE_2D, 0);
  113. if (frame_buffer_object_)
  114. glDeleteFramebuffersEXT(1, &frame_buffer_object_);
  115. if (texture_id_)
  116. glDeleteTextures(1, &texture_id_);
  117. }
  118. GLuint frame_buffer_object() const { return frame_buffer_object_; }
  119. GLuint texture_id() const { return texture_id_; }
  120. gfx::Size size() const { return size_; }
  121. private:
  122. GLuint frame_buffer_object_ = 0;
  123. GLuint texture_id_ = 0;
  124. gfx::Size size_;
  125. };
  126. AwGLSurfaceExternalStencil::AwGLSurfaceExternalStencil(
  127. gl::GLDisplayEGL* display,
  128. bool is_angle)
  129. : AwGLSurface(display, is_angle) {}
  130. AwGLSurfaceExternalStencil::~AwGLSurfaceExternalStencil() = default;
  131. unsigned int AwGLSurfaceExternalStencil::GetBackingFramebufferObject() {
  132. const auto& stencil_state =
  133. android_webview::ScopedAppGLStateRestore::Current()->stencil_state();
  134. if (stencil_state.stencil_test_enabled) {
  135. // Framebuffer was created during RecalculateClipAndTransform();
  136. DCHECK(framebuffer_);
  137. return framebuffer_->frame_buffer_object();
  138. }
  139. return AwGLSurface::GetBackingFramebufferObject();
  140. }
  141. gfx::SwapResult AwGLSurfaceExternalStencil::SwapBuffers(
  142. PresentationCallback callback) {
  143. const auto& stencil_state =
  144. android_webview::ScopedAppGLStateRestore::Current()->stencil_state();
  145. if (stencil_state.stencil_test_enabled) {
  146. DCHECK(framebuffer_);
  147. DCHECK(blit_context_);
  148. // Flush skia renderer rendering. This is working around what appears to be
  149. // a driver bug that causes rendering to break.
  150. glFlush();
  151. // Restore stencil state.
  152. glEnable(GL_STENCIL_TEST);
  153. glStencilFuncSeparate(GL_FRONT, stencil_state.stencil_front_func,
  154. stencil_state.stencil_front_mask,
  155. stencil_state.stencil_front_ref);
  156. glStencilFuncSeparate(GL_BACK, stencil_state.stencil_back_func,
  157. stencil_state.stencil_back_mask,
  158. stencil_state.stencil_back_ref);
  159. glStencilMaskSeparate(GL_FRONT, stencil_state.stencil_front_writemask);
  160. glStencilMaskSeparate(GL_BACK, stencil_state.stencil_back_writemask);
  161. glStencilOpSeparate(GL_FRONT, stencil_state.stencil_front_fail_op,
  162. stencil_state.stencil_front_z_fail_op,
  163. stencil_state.stencil_front_z_pass_op);
  164. glStencilOpSeparate(GL_BACK, stencil_state.stencil_back_fail_op,
  165. stencil_state.stencil_back_z_fail_op,
  166. stencil_state.stencil_back_z_pass_op);
  167. // Bind required context.
  168. blit_context_->Bind();
  169. // Bind real frame buffer.
  170. glBindFramebufferEXT(GL_FRAMEBUFFER,
  171. AwGLSurface::GetBackingFramebufferObject());
  172. // Scale clip rect to (0, 0)x(1, 1) space.
  173. gfx::QuadF quad = gfx::QuadF(gfx::RectF(clip_rect_));
  174. quad.Scale(1.0 / viewport_.width(), 1.0 / viewport_.height());
  175. gfx::QuadF tex_quad = gfx::QuadF(gfx::RectF(1.0, 1.0));
  176. // Set-up vertex attributes. p1-p4-p2-p3 forms triangle strip for quad.
  177. // clang-format off
  178. const gfx::PointF data[8] = {quad.p1(), tex_quad.p1(),
  179. quad.p4(), tex_quad.p4(),
  180. quad.p2(), tex_quad.p2(),
  181. quad.p3(), tex_quad.p3()};
  182. // clang-format on
  183. glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(gfx::PointF) * 2,
  184. &data[1]);
  185. glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(gfx::PointF) * 2,
  186. &data[0]);
  187. glActiveTexture(GL_TEXTURE0);
  188. glBindTexture(GL_TEXTURE_2D, framebuffer_->texture_id());
  189. if (gl::g_current_gl_driver->fn.glBindSamplerFn)
  190. glBindSampler(0, 0);
  191. // We need to restore viewport as it might have changed by renderer
  192. glViewport(0, 0, viewport_.width(), viewport_.height());
  193. // We draw only inside clip rect, no need to scissor.
  194. glDisable(GL_SCISSOR_TEST);
  195. // Restore color mask in case.
  196. glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  197. // Restore blending.
  198. glEnable(GL_BLEND);
  199. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  200. glDisable(GL_CULL_FACE);
  201. glDisable(GL_DEPTH_TEST);
  202. glFrontFace(GL_CCW);
  203. if (gl::g_current_gl_driver->fn.glWindowRectanglesEXTFn)
  204. glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 0, nullptr);
  205. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  206. }
  207. return AwGLSurface::SwapBuffers(std::move(callback));
  208. }
  209. void AwGLSurfaceExternalStencil::RecalculateClipAndTransform(
  210. gfx::Size* viewport,
  211. gfx::Rect* clip_rect,
  212. gfx::Transform* transform) {
  213. clip_rect_ = *clip_rect;
  214. viewport_ = *viewport;
  215. const auto& stencil_state =
  216. android_webview::ScopedAppGLStateRestore::Current()->stencil_state();
  217. if (stencil_state.stencil_test_enabled) {
  218. // Initialize graphics part needed for blit with stencil here so we don't
  219. // change any gl state after GrContext reset after this function call.
  220. if (!blit_context_) {
  221. blit_context_ = std::make_unique<BlitContext>();
  222. }
  223. if (!framebuffer_ || framebuffer_->size() != clip_rect_.size()) {
  224. // Delete old one first to reduce peak memory.
  225. framebuffer_.reset();
  226. framebuffer_ = std::make_unique<FrameBuffer>(clip_rect_.size());
  227. }
  228. framebuffer_->Clear();
  229. // Adjust transform, clip rect and viewport to be in original clip rect
  230. // space as we will draw to FBO of clip_rect size and blit it to screen at
  231. // original location.
  232. transform->PostTranslate(-clip_rect->x(), -clip_rect->y());
  233. clip_rect->set_origin(gfx::Point(0, 0));
  234. *viewport = clip_rect_.size();
  235. } else {
  236. // We're not going to draw to frame buffer, so we can free it to save
  237. // memory, assuming |stencil_test_enabled| doesn't change often.
  238. framebuffer_.reset();
  239. }
  240. }
  241. bool AwGLSurfaceExternalStencil::IsDrawingToFBO() {
  242. const auto& stencil_state =
  243. android_webview::ScopedAppGLStateRestore::Current()->stencil_state();
  244. return stencil_state.stencil_test_enabled;
  245. }
  246. } // namespace android_webview