2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/render/renderers/scene-graph-image-renderer.h>
22 #include <dali/public-api/common/dali-common.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/internal/common/internal-constants.h>
25 #include <dali/internal/render/common/culling-algorithms.h>
26 #include <dali/internal/render/common/performance-monitor.h>
27 #include <dali/internal/render/common/vertex.h>
28 #include <dali/internal/render/gl-resources/gpu-buffer.h>
29 #include <dali/internal/render/gl-resources/texture.h>
30 #include <dali/internal/render/gl-resources/texture-cache.h>
31 #include <dali/internal/render/gl-resources/texture-units.h>
32 #include <dali/internal/render/renderers/scene-graph-renderer-debug.h>
33 #include <dali/internal/render/shaders/program.h>
34 #include <dali/internal/render/shaders/shader.h>
35 #include <dali/internal/update/controllers/scene-controller.h>
39 #if defined(DEBUG_ENABLED)
40 Debug::Filter* gImageRenderFilter=Debug::Filter::New(Debug::NoLogging, false, "LOG_IMAGE_RENDERER");
44 * VertexToTextureCoord
45 * Represents a mapping between a 1 dimensional vertex coordinate
46 * and a 1 dimensional texture coordinate.
48 struct VertexToTextureCoord
51 * @param[in] xVertex Vertex coordinate
52 * @param[in] uTexture Texture coordinate
54 VertexToTextureCoord(float xVertex, float uTexture)
60 float x; ///< 1D Vertex position
61 float u; ///< 1D Texture position
65 * Generates a list of equally spaced intervals along a line, including
66 * intervals at the points specified in insertionList.
67 * The line starts from insertionList.begin() and ends at insertionList.end()-1
68 * The number of intervals and spacing of these intervals is specified by
70 * @param[out] intervalList An empty vector to be populated with the list of intervals.
71 * @param[in] intervals The number of intervals to be generated.
72 * @param[in] insertionList A vector containing the points on the line to be inserted.
74 void GenerateIntervals(std::vector<VertexToTextureCoord>& intervalList, int intervals, const std::vector<VertexToTextureCoord>& insertionList)
76 DALI_ASSERT_DEBUG(insertionList.size() >= 2);
77 DALI_ASSERT_DEBUG(intervals > 0);
79 std::vector<VertexToTextureCoord>::const_iterator iter = insertionList.begin();
80 if( iter != insertionList.end() )
82 std::vector<VertexToTextureCoord>::const_iterator end = insertionList.end()-1;
84 const float length = end->x - iter->x;
85 const float intervalSize = length / static_cast<float>(intervals);
88 for(;iter!=end;++iter)
95 for(;x<x1;x+=intervalSize)
97 float progress = (x - x0) / (x1 - x0); // progress value between current interval and next.
98 float u = u0 + (u1 - u0) * progress; // u 1D texture coordinate value for this x position.
99 intervalList.push_back( VertexToTextureCoord( x, u ) );
101 intervalList.push_back( VertexToTextureCoord( x1, u1 ) );
117 ImageRenderer* ImageRenderer::New( RenderDataProvider& dataprovider )
119 return new ImageRenderer( dataprovider );
122 ImageRenderer::~ImageRenderer()
124 if ( mTextureId > 0 )
126 mTextureCache->RemoveObserver(mTextureId, this);
132 void ImageRenderer::SetTextureId( ResourceId textureId )
134 if ( mTextureId > 0 )
136 mTextureCache->RemoveObserver(mTextureId, this);
139 mTextureId = textureId;
144 mTextureCache->AddObserver(textureId, this);
148 void ImageRenderer::SetPixelArea( const ImageRenderer::PixelArea& pixelArea )
150 mUsePixelArea = true;
151 mPixelArea = pixelArea;
152 mIsMeshGenerated = false;
155 void ImageRenderer::SetNinePatchBorder( const Vector4& border, bool inPixels )
158 mBorderInPixels = inPixels;
159 mIsMeshGenerated = false;
162 void ImageRenderer::CalculateMeshData( MeshType type, const Vector2& targetSize, bool usePixelArea )
165 mGeometrySize = targetSize;
166 mUsePixelArea = usePixelArea;
167 mIsMeshGenerated = false;
170 void ImageRenderer::TextureDiscarded( ResourceId textureId )
172 DALI_ASSERT_DEBUG( mTextureId == textureId || mTextureId == 0 );
178 void ImageRenderer::GlContextDestroyed()
182 mVertexBuffer->GlContextDestroyed();
186 mIndexBuffer->GlContextDestroyed();
188 // force recreation of the geometry in next render
189 mIsMeshGenerated = false;
192 void ImageRenderer::GlCleanup()
194 DALI_LOG_INFO( gImageRenderFilter, Debug::Verbose, "GlCleanup() textureId=%d texture:%p\n", mTextureId, mTexture);
196 mVertexBuffer.Reset();
197 mIndexBuffer.Reset();
200 bool ImageRenderer::RequiresDepthTest() const
205 bool ImageRenderer::CheckResources()
207 if( mTexture == NULL )
209 mTexture = mTextureCache->GetTexture( mTextureId );
212 if( mTexture == NULL )
217 if( ( mTexture->GetWidth() <= 0u ) ||
218 ( mTexture->GetHeight() <= 0u ) )
223 Integration::ResourceId shaderTextureId = mShader->GetTextureIdToRender() ;
225 if( shaderTextureId && mTextureCache->GetTexture( shaderTextureId ) == NULL )
233 void ImageRenderer::ResolveGeometryTypes( BufferIndex bufferIndex, GeometryType& outType, ShaderSubTypes& outSubType )
235 outType = GEOMETRY_TYPE_IMAGE;
236 outSubType = SHADER_DEFAULT;
239 bool ImageRenderer::IsOutsideClipSpace( const Matrix& modelMatrix, const Matrix& modelViewProjectionMatrix )
241 mContext->IncrementRendererCount();
243 Rect<float> boundingBox( mGeometrySize.x * -0.5f, mGeometrySize.y * -0.5f, mGeometrySize.x, mGeometrySize.y );
245 DEBUG_BOUNDING_BOX( *mContext, boundingBox, modelViewProjectionMatrix );
247 if(Is2dBoxOutsideClipSpace( modelMatrix, modelViewProjectionMatrix, boundingBox ) )
249 mContext->IncrementCulledCount();
255 void ImageRenderer::DoRender( BufferIndex bufferIndex, Program& program, const Matrix& modelViewMatrix, const Matrix& viewMatrix )
257 DALI_LOG_INFO( gImageRenderFilter, Debug::Verbose, "DoRender() textureId=%d texture:%p\n", mTextureId, mTexture);
259 DALI_ASSERT_DEBUG( 0 != mTextureId && "ImageRenderer::DoRender. mTextureId == 0." );
260 DALI_ASSERT_DEBUG( NULL != mTexture && "ImageRenderer::DoRender. mTexture == NULL." );
262 if(! mIsMeshGenerated )
264 GenerateMeshData( mTexture );
267 DALI_ASSERT_DEBUG( mVertexBuffer );
269 mTextureCache->BindTexture( mTexture, mTextureId, GL_TEXTURE_2D, TextureUnitAsGLenum( TEXTURE_UNIT_IMAGE ) );
271 if( mTexture->GetTextureId() == 0 )
273 return; // early out if we haven't got a GL texture yet (e.g. due to context loss)
276 mTexture->ApplySampler( mSamplerBitfield );
278 // Set sampler uniform
279 GLint samplerLoc = program.GetUniformLocation( Program::UNIFORM_SAMPLER );
280 if( -1 != samplerLoc )
283 program.SetUniform1i( samplerLoc, TEXTURE_UNIT_IMAGE );
286 // make sure the vertex is bound, this has to be done before
287 // we call VertexAttribPointer otherwise you get weird output on the display
288 mVertexBuffer->Bind();
290 samplerLoc = program.GetUniformLocation( Program::UNIFORM_SAMPLER_RECT );
291 if( -1 != samplerLoc )
297 mTexture->GetTextureCoordinates( uv, &mPixelArea );
301 mTexture->GetTextureCoordinates( uv, NULL );
305 program.SetUniform4f( samplerLoc, uv.u0, uv.v0, uv.u2, uv.v2 );
308 // Check whether the program supports the expected attributes/uniforms
309 const GLint positionLoc = program.GetAttribLocation( Program::ATTRIB_POSITION );
310 const GLint texCoordLoc = program.GetAttribLocation( Program::ATTRIB_TEXCOORD );
312 if ( positionLoc != -1 )
314 mContext->EnableVertexAttributeArray( positionLoc );
316 const int stride = 4 * sizeof(float);
317 mContext->VertexAttribPointer( positionLoc, 2, GL_FLOAT, GL_FALSE, stride, 0 );
320 if ( texCoordLoc != -1 )
322 mContext->EnableVertexAttributeArray( texCoordLoc );
324 const int stride = 4 * sizeof(float);
325 mContext->VertexAttribPointer( texCoordLoc, 2, GL_FLOAT, GL_FALSE, stride, (const void*) (sizeof(float)*2) );
332 case NINE_PATCH_NO_CENTER:
334 const GLsizei vertexCount = mVertexBuffer->GetBufferSize() / sizeof(Vertex2D); // compiler will optimize this to >> if possible
335 mContext->DrawArrays( GL_TRIANGLE_STRIP, 0, vertexCount );
336 DRAW_ARRAY_RECORD( vertexCount );
340 case GRID_NINE_PATCH:
341 case GRID_NINE_PATCH_NO_CENTER:
343 const GLsizei indexCount = mIndexBuffer->GetBufferSize() / sizeof(GLushort); // compiler will optimize this to >> if possible
344 mIndexBuffer->Bind();
345 mContext->DrawElements( GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0 );
346 DRAW_ELEMENT_RECORD( indexCount );
351 if ( positionLoc != -1 )
353 mContext->DisableVertexAttributeArray( positionLoc );
356 if ( texCoordLoc != -1 )
358 mContext->DisableVertexAttributeArray( texCoordLoc );
362 void ImageRenderer::UpdateVertexBuffer( GLsizeiptr size, const GLvoid *data )
364 // create/destroy if needed/not needed.
365 if ( size && !mVertexBuffer )
367 mVertexBuffer = new GpuBuffer( *mContext, GpuBuffer::ARRAY_BUFFER, GpuBuffer::DYNAMIC_DRAW );
369 else if ( !size && mVertexBuffer )
371 mVertexBuffer.Reset();
377 mVertexBuffer->UpdateDataBuffer( size, data );
381 void ImageRenderer::UpdateIndexBuffer( GLsizeiptr size, const GLvoid *data )
383 // create/destroy if needed/not needed.
384 if ( size && !mIndexBuffer )
386 mIndexBuffer = new GpuBuffer( *mContext, GpuBuffer::ELEMENT_ARRAY_BUFFER, GpuBuffer::STATIC_DRAW );
388 else if ( !size && mIndexBuffer )
390 mIndexBuffer.Reset();
396 mIndexBuffer->UpdateDataBuffer(size,data);
400 void ImageRenderer::GenerateMeshData( Texture* texture )
402 const PixelArea* pixelArea = NULL;
405 pixelArea = &mPixelArea;
410 case ImageRenderer::QUAD:
412 SetQuadMeshData( texture, mGeometrySize, pixelArea );
415 case ImageRenderer::NINE_PATCH:
417 SetNinePatchMeshData( texture, mGeometrySize, mBorder, mBorderInPixels, pixelArea, false );
420 case ImageRenderer::NINE_PATCH_NO_CENTER:
422 SetNinePatchMeshData( texture, mGeometrySize, mBorder, mBorderInPixels, pixelArea, true );
425 case ImageRenderer::GRID_QUAD:
427 SetGridMeshData( texture, mGeometrySize, NULL, false, pixelArea );
430 case ImageRenderer::GRID_NINE_PATCH:
431 case ImageRenderer::GRID_NINE_PATCH_NO_CENTER:
433 SetGridMeshData( texture, mGeometrySize, &mBorder, mBorderInPixels, pixelArea );
437 mIsMeshGenerated = true;
440 void ImageRenderer::SetQuadMeshData( Texture* texture, const Vector2& size, const PixelArea* pixelArea )
442 const float x0 = -0.5f * size.x;
443 const float y0 = -0.5f * size.y;
444 const float x1 = 0.5f * size.x;
445 const float y1 = 0.5f * size.y;
448 * Here we render the square as a single square, as texture
449 * coordinates linearly interpolate between the 4 vertices.
451 * Note: a square (or a quad) is rendered as 2 triangles.
452 * Vertices 0,1,2 represent triangle A.
453 * Vertices 1,2,3 represent triangle B.
455 * No indices are needed as we tell GL to render in strip mode
456 * (GL_TRIANGLE_STRIP), which is faster, and consumes less
479 // We may only be displaying an area of the texture.
480 // Calling MapUV converts the u,v values to correct values for the pixel area
482 texture->MapUV( sizeof(verts)/sizeof(Vertex2D), verts, pixelArea );
484 UpdateVertexBuffer( sizeof(verts), verts );
485 UpdateIndexBuffer( 0, NULL );
488 void ImageRenderer::SetNinePatchMeshData( Texture* texture, const Vector2& size, const Vector4& border, bool borderInPixels, const PixelArea* pixelArea, bool noCenter )
490 DALI_ASSERT_ALWAYS( mTexture->GetWidth() > 0.0f && "Invalid Texture width" );
491 DALI_ASSERT_ALWAYS( mTexture->GetHeight() > 0.0f && "Invalid Texture height" );
493 float textureWidth = mTexture->GetWidth();
494 float textureHeight = mTexture->GetHeight();
496 float borderLeft, borderTop, borderRight, borderBottom; // pixels from edge
497 float borderX0, borderY0, borderX1, borderY1; // In the range 0 -> 1
499 if ( borderInPixels )
501 borderLeft = border.x;
502 borderTop = border.y;
503 borderRight = border.z;
504 borderBottom = border.w;
506 borderX0 = border.x / textureWidth;
507 borderY0 = border.y / textureHeight;
508 borderX1 = 1.0f - ( border.z / textureWidth );
509 borderY1 = 1.0f - ( border.w / textureHeight );
513 borderLeft = textureWidth * border.x;
514 borderTop = textureHeight * border.y;
515 borderRight = textureWidth * (1.0f - border.z);
516 borderBottom = textureHeight * (1.0f - border.w);
524 const float u0 = 0.0f;
525 const float u3 = 1.0f;
526 const float u1 = borderX0;
527 const float u2 = borderX1;
529 const float v0 = 0.0f;
530 const float v3 = 1.0f;
531 const float v1 = borderY0;
532 const float v2 = borderY1;
534 const float x0 = size.x * -0.5;
535 const float x1 = x0 + borderLeft;
536 const float x2 = x0 + size.x - borderRight;
537 const float x3 = x0 + size.x;
539 const float y0 = size.y * -0.5;
540 const float y1 = y0 + borderTop;
541 const float y2 = y0 + size.y - borderBottom;
542 const float y3 = y0 + size.y;
548 * We're breaking a quad in to 9 smaller quads, so that when it's
549 * stretched the corners maintain their size.
550 * For speed the 9-patch is drawn with a single triangle span, the draw
551 * order of the span is 1->9.
552 * Previously it would draw three separate spans (1->3, 4->6, 7->9), but now it
553 * it does it one go by turning the corner when gets to the end of each row.
555 * No indices are needed as we tell GL to render in strip mode
556 * (GL_TRIANGLE_STRIP), which is faster, and consumes less
559 * |---|---------------|---|
561 * |---|---------------|---|
566 * |---|---------------|---|
568 * |---|---------------|---|
571 Vertex2D vertsWithCenter[]={
622 const size_t vertsSize = sizeof( vertsWithCenter );
623 const unsigned int vertexCount = vertsSize / sizeof( vertsWithCenter[0] );
624 texture->MapUV( vertexCount, vertsWithCenter, pixelArea );
625 UpdateVertexBuffer( vertsSize, vertsWithCenter );
630 * The center part is not going to be rendered. The 9-patch border is drawn with
631 * a single triangle span, and the draw order of the span is 1->8.
633 * |---|---------------|---|
635 * |---|---------------|---|
639 * | | (not rendered)| |
641 * |---|---------------|---|
643 * |---|---------------|---|
646 Vertex2D vertsWithNoCenter[]={
661 // reset the starting point to x3, y1
684 // reset point to x0,y3
689 // top left box (starting from (x0,y3)) (7)
703 const size_t vertsSize = sizeof( vertsWithNoCenter );
704 const unsigned int vertexCount = vertsSize / sizeof( vertsWithNoCenter[0] );
705 texture->MapUV( vertexCount, vertsWithNoCenter, pixelArea );
706 UpdateVertexBuffer( vertsSize, vertsWithNoCenter );
708 // not using an index buffer
709 UpdateIndexBuffer( 0, NULL );
713 void ImageRenderer::SetGridMeshData( Texture* texture, const Vector2& size, const Vector4* border, bool borderInPixels, const PixelArea* pixelArea )
717 * In Grid Mode, we tessellate the single quad into smaller quads
718 * at approximately (guideGridSize x guideGridSize) in size.
720 * Conversion of Quad to Gridded Quad.
722 * |-----------| |---|---|---|
724 * | | -> |---|---|---|
726 * |-----------| |---|---|---|
729 * In Grid Mode, we tessellate each quad of a 9-patch
730 * (see SetNinePatchMeshData) into smaller quads at approximately
731 * (guideGridSize x guideGridSize) in size.
733 * This satisfies the two requirements of a 9-patch with grid:
735 * 1. Texture coordinates within each section of the 9-patch
736 * should change linearly to that 9-patch's rules.
737 * 2. The image as as whole should provide Vertex points at
738 * approximate guideGridSize intervals.
740 * The result should be the horizontal and vertical lines of
741 * a 9-patch overlayed by the horizontal and vertical lines of
745 * | | | | | | | <- Grid Markers
746 * -|-------|-------------------|-------|
750 * |-------+-------------------+-------|
755 * -| 4 | 5 | 6 | <- 9 Patch.
760 * |-------+-------------------+-------|
764 * -|-------|-------------------|-------|
768 * | | | | | | | <- Grid Markers
769 * -|-------|-------------------|-------|
772 * -|-----|-|---|-----|-----|---|-|-----|
773 * |-------+-------------------+-------|
775 * -|-----|-|---|-----|-----|---|-|-----|
778 * -|-----|-|---|-----|-----|---|-|-----| <- 9 Patch.
781 * -|-----|-|---|-----|-----|---|-|-----|
783 * |-------+-------------------+-------|
784 * -|-----|-|---|-----|-----|---|-|-----|
787 * -|-------|-------------------|-------|
790 std::vector<VertexToTextureCoord> horizontalDivisions;
791 std::vector<VertexToTextureCoord> verticalDivisions;
793 const float guideGridSize = mShader->GetGridDensity();
795 const int textureWidth = texture->GetWidth();
796 const int textureHeight = texture->GetHeight();
798 const float halfWidth = size.width * 0.5f;
799 const float halfHeight = size.height * 0.5f;
801 // Determine how many rectangles across and down to tesselate image into.
802 const int guideRectX = (size.width / guideGridSize);
803 const int guideRectY = (size.height / guideGridSize);
805 // Build up list of points in X axis where vertices need to go.
806 std::vector<VertexToTextureCoord> insertionList;
807 insertionList.reserve(4);
808 insertionList.push_back( VertexToTextureCoord( -halfWidth, 0.0f ) );
810 // If 9-Patch Border exists, add additional border points in list.
813 float borderX0, borderX1, borderU0, borderU1;
815 if ( borderInPixels )
817 borderX0 = border->x - halfWidth;
818 borderX1 = halfWidth - border->z;
820 borderU0 = border->x / textureWidth;
821 borderU1 = 1.0f - (border->z / textureWidth);
825 borderX0 = border->x * textureWidth - halfWidth;
826 borderX1 = halfWidth - (1.0f - border->z) * textureWidth;
827 borderU0 = border->x;
828 borderU1 = border->z;
831 insertionList.push_back( VertexToTextureCoord( borderX0, borderU0 ) );
832 insertionList.push_back( VertexToTextureCoord( borderX1, borderU1 ) );
835 insertionList.push_back( VertexToTextureCoord( halfWidth, 1.0f ) );
836 GenerateIntervals(horizontalDivisions, guideRectX + 2, insertionList);
838 // Build up list of points in Y axis where vertices need to go.
839 insertionList.clear();
840 insertionList.push_back( VertexToTextureCoord( -halfHeight, 0.0f ) );
842 // If 9-Patch Border exists, add additional border points in list.
845 float borderY0, borderY1, borderU0, borderU1;
847 if ( borderInPixels )
849 borderY0 = border->y - halfHeight;
850 borderY1 = halfHeight - border->w;
852 borderU0 = border->y / textureHeight;
853 borderU1 = 1.0f - (border->w / textureHeight);
857 borderY0 = border->y * textureHeight - halfHeight;
858 borderY1 = halfHeight - (1.0f - border->w) * textureHeight;
860 borderU0 = border->y;
861 borderU1 = border->w;
864 insertionList.push_back( VertexToTextureCoord( borderY0, borderU0 ) );
865 insertionList.push_back( VertexToTextureCoord( borderY1, borderU1 ) );
868 insertionList.push_back( VertexToTextureCoord( halfHeight, 1.0f ) );
869 GenerateIntervals(verticalDivisions, guideRectY + 2, insertionList);
871 // Now build up Vertex pattern based on the above X and Y lists.
872 const int totalVertices = horizontalDivisions.size() * verticalDivisions.size();
873 Vertex2D* vertices = new Vertex2D[totalVertices];
874 Vertex2D* vertex = vertices;
876 for(std::vector<VertexToTextureCoord>::const_iterator yIter = verticalDivisions.begin(); yIter != verticalDivisions.end(); ++yIter )
878 for(std::vector<VertexToTextureCoord>::const_iterator xIter = horizontalDivisions.begin(); xIter != horizontalDivisions.end(); ++xIter )
880 vertex->mX = xIter->x;
881 vertex->mU = xIter->u;
882 vertex->mY = yIter->x;
883 vertex->mV = yIter->u;
888 // Build up Triangle indicies, very predictable pattern.
889 const size_t rectX = horizontalDivisions.size() - 1;
890 const size_t rectY = verticalDivisions.size() - 1;
891 const size_t totalIndices = rectX * rectY * 6; // 2 triangles per quad (rect) and 3 points to define each triangle.
892 GLushort* indices = new GLushort[totalIndices];
894 GenerateMeshIndices(indices, rectX, rectY);
896 texture->MapUV( totalVertices, vertices, pixelArea );
898 UpdateVertexBuffer( totalVertices * sizeof(Vertex2D) , vertices );
899 UpdateIndexBuffer( totalIndices * sizeof(GLushort), indices );
905 void ImageRenderer::GenerateMeshIndices(GLushort* indices, int rectanglesX, int rectanglesY)
907 GLushort *i = indices;
908 const int meshEndIndex = rectanglesY * (rectanglesX + 1);
911 while(index < meshEndIndex)
913 const int rowEndIndex = index + rectanglesX;
914 for (; index < rowEndIndex; index++ )
917 *i++ = index + 1 + rectanglesX;
921 *i++ = index + 1 + rectanglesX;
922 *i++ = index + 2 + rectanglesX;
924 index++; // one extra vertex per row than rects.
928 ImageRenderer::ImageRenderer( RenderDataProvider& dataprovider )
929 : Renderer( dataprovider ),
931 mBorder( 0.45, 0.45, 0.1, 0.1 ),
935 mMeshType( ImageRenderer::QUAD ),
936 mIsMeshGenerated( false ),
937 mBorderInPixels( false ),
938 mUsePixelArea( false )
942 } // namespace SceneGraph
944 } // namespace Internal