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/scene-graph-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( NodeDataProvider& dataProvider )
119 return new ImageRenderer( dataProvider );
122 ImageRenderer::~ImageRenderer()
124 if ( mTextureId > 0 )
126 mTextureCacheDELETEME->RemoveObserver(mTextureId, this);
132 void ImageRenderer::SetTextureId( ResourceId textureId )
134 if ( mTextureId > 0 )
136 mTextureCacheDELETEME->RemoveObserver(mTextureId, this);
139 mTextureId = textureId;
144 mTextureCacheDELETEME->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 = mTextureCacheDELETEME->GetTexture( mTextureId );
212 if( mTexture == NULL )
217 if( ( mTexture->GetWidth() <= 0u ) ||
218 ( mTexture->GetHeight() <= 0u ) )
223 Integration::ResourceId shaderTextureId = mShader->GetTextureIdToRender() ;
225 if( shaderTextureId && mTextureCacheDELETEME->GetTexture( shaderTextureId ) == NULL )
233 bool ImageRenderer::IsOutsideClipSpace( Context& context, const Matrix& modelMatrix, const Matrix& modelViewProjectionMatrix )
235 context.IncrementRendererCount();
237 Rect<float> boundingBox( mGeometrySize.x * -0.5f, mGeometrySize.y * -0.5f, mGeometrySize.x, mGeometrySize.y );
239 DEBUG_BOUNDING_BOX( *mContext, boundingBox, modelViewProjectionMatrix );
241 if(Is2dBoxOutsideClipSpace( modelMatrix, modelViewProjectionMatrix, boundingBox ) )
243 context.IncrementCulledCount();
249 void ImageRenderer::DoRender( Context& context, TextureCache& textureCache, BufferIndex bufferIndex, Program& program, const Matrix& modelViewMatrix, const Matrix& viewMatrix )
251 DALI_LOG_INFO( gImageRenderFilter, Debug::Verbose, "DoRender() textureId=%d texture:%p\n", mTextureId, mTexture);
253 DALI_ASSERT_DEBUG( 0 != mTextureId && "ImageRenderer::DoRender. mTextureId == 0." );
254 DALI_ASSERT_DEBUG( NULL != mTexture && "ImageRenderer::DoRender. mTexture == NULL." );
256 if(! mIsMeshGenerated )
258 GenerateMeshData( mTexture );
261 DALI_ASSERT_DEBUG( mVertexBuffer );
263 mTextureCacheDELETEME->BindTexture( mTexture, mTextureId, GL_TEXTURE_2D, TEXTURE_UNIT_IMAGE );
265 if( mTexture->GetTextureId() == 0 )
267 return; // early out if we haven't got a GL texture yet (e.g. due to context loss)
270 mTexture->ApplySampler( TEXTURE_UNIT_IMAGE, mSamplerBitfield );
272 // Set sampler uniform
273 GLint samplerLoc = program.GetUniformLocation( Program::UNIFORM_SAMPLER );
274 if( -1 != samplerLoc )
277 program.SetUniform1i( samplerLoc, TEXTURE_UNIT_IMAGE );
280 // make sure the vertex is bound, this has to be done before
281 // we call VertexAttribPointer otherwise you get weird output on the display
282 mVertexBuffer->Bind();
284 samplerLoc = program.GetUniformLocation( Program::UNIFORM_SAMPLER_RECT );
285 if( -1 != samplerLoc )
291 mTexture->GetTextureCoordinates( uv, &mPixelArea );
295 mTexture->GetTextureCoordinates( uv, NULL );
299 program.SetUniform4f( samplerLoc, uv.u0, uv.v0, uv.u2, uv.v2 );
302 // Check whether the program supports the expected attributes/uniforms
303 const GLint positionLoc = program.GetAttribLocation( Program::ATTRIB_POSITION );
304 const GLint texCoordLoc = program.GetAttribLocation( Program::ATTRIB_TEXCOORD );
306 if ( positionLoc != -1 )
308 context.EnableVertexAttributeArray( positionLoc );
310 const int stride = 4 * sizeof(float);
311 context.VertexAttribPointer( positionLoc, 2, GL_FLOAT, GL_FALSE, stride, 0 );
314 if ( texCoordLoc != -1 )
316 context.EnableVertexAttributeArray( texCoordLoc );
318 const int stride = 4 * sizeof(float);
319 context.VertexAttribPointer( texCoordLoc, 2, GL_FLOAT, GL_FALSE, stride, (const void*) (sizeof(float)*2) );
326 case NINE_PATCH_NO_CENTER:
328 const GLsizei vertexCount = mVertexBuffer->GetBufferSize() / sizeof(Vertex2D); // compiler will optimize this to >> if possible
329 context.DrawArrays( GL_TRIANGLE_STRIP, 0, vertexCount );
333 case GRID_NINE_PATCH:
334 case GRID_NINE_PATCH_NO_CENTER:
336 const GLsizei indexCount = mIndexBuffer->GetBufferSize() / sizeof(GLushort); // compiler will optimize this to >> if possible
337 mIndexBuffer->Bind();
338 context.DrawElements( GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0 );
343 if ( positionLoc != -1 )
345 context.DisableVertexAttributeArray( positionLoc );
348 if ( texCoordLoc != -1 )
350 context.DisableVertexAttributeArray( texCoordLoc );
354 void ImageRenderer::UpdateVertexBuffer( Context& context, GLsizeiptr size, const GLvoid *data )
356 // create/destroy if needed/not needed.
357 if ( size && !mVertexBuffer )
359 mVertexBuffer = new GpuBuffer( context, GpuBuffer::ARRAY_BUFFER, GpuBuffer::DYNAMIC_DRAW );
361 else if ( !size && mVertexBuffer )
363 mVertexBuffer.Reset();
369 mVertexBuffer->UpdateDataBuffer( size, data );
373 void ImageRenderer::UpdateIndexBuffer( Context& context, GLsizeiptr size, const GLvoid *data )
375 // create/destroy if needed/not needed.
376 if ( size && !mIndexBuffer )
378 mIndexBuffer = new GpuBuffer( context, GpuBuffer::ELEMENT_ARRAY_BUFFER, GpuBuffer::STATIC_DRAW );
380 else if ( !size && mIndexBuffer )
382 mIndexBuffer.Reset();
388 mIndexBuffer->UpdateDataBuffer(size,data);
392 void ImageRenderer::GenerateMeshData( Texture* texture )
394 const PixelArea* pixelArea = NULL;
397 pixelArea = &mPixelArea;
402 case ImageRenderer::QUAD:
404 SetQuadMeshData( texture, mGeometrySize, pixelArea );
407 case ImageRenderer::NINE_PATCH:
409 SetNinePatchMeshData( texture, mGeometrySize, mBorder, mBorderInPixels, pixelArea, false );
412 case ImageRenderer::NINE_PATCH_NO_CENTER:
414 SetNinePatchMeshData( texture, mGeometrySize, mBorder, mBorderInPixels, pixelArea, true );
417 case ImageRenderer::GRID_QUAD:
419 SetGridMeshData( texture, mGeometrySize, NULL, false, pixelArea );
422 case ImageRenderer::GRID_NINE_PATCH:
423 case ImageRenderer::GRID_NINE_PATCH_NO_CENTER:
425 SetGridMeshData( texture, mGeometrySize, &mBorder, mBorderInPixels, pixelArea );
429 mIsMeshGenerated = true;
432 void ImageRenderer::SetQuadMeshData( Texture* texture, const Vector2& size, const PixelArea* pixelArea )
434 const float x0 = -0.5f * size.x;
435 const float y0 = -0.5f * size.y;
436 const float x1 = 0.5f * size.x;
437 const float y1 = 0.5f * size.y;
440 * Here we render the square as a single square, as texture
441 * coordinates linearly interpolate between the 4 vertices.
443 * Note: a square (or a quad) is rendered as 2 triangles.
444 * Vertices 0,1,2 represent triangle A.
445 * Vertices 1,2,3 represent triangle B.
447 * No indices are needed as we tell GL to render in strip mode
448 * (GL_TRIANGLE_STRIP), which is faster, and consumes less
471 // We may only be displaying an area of the texture.
472 // Calling MapUV converts the u,v values to correct values for the pixel area
474 texture->MapUV( sizeof(verts)/sizeof(Vertex2D), verts, pixelArea );
476 UpdateVertexBuffer( *mContextDELETEME, sizeof(verts), verts );
477 UpdateIndexBuffer( *mContextDELETEME, 0, NULL );
480 void ImageRenderer::SetNinePatchMeshData( Texture* texture, const Vector2& size, const Vector4& border, bool borderInPixels, const PixelArea* pixelArea, bool noCenter )
482 DALI_ASSERT_ALWAYS( mTexture->GetWidth() > 0.0f && "Invalid Texture width" );
483 DALI_ASSERT_ALWAYS( mTexture->GetHeight() > 0.0f && "Invalid Texture height" );
485 float textureWidth = mTexture->GetWidth();
486 float textureHeight = mTexture->GetHeight();
488 float borderLeft, borderTop, borderRight, borderBottom; // pixels from edge
489 float borderX0, borderY0, borderX1, borderY1; // In the range 0 -> 1
491 if ( borderInPixels )
493 borderLeft = border.x;
494 borderTop = border.y;
495 borderRight = border.z;
496 borderBottom = border.w;
498 borderX0 = border.x / textureWidth;
499 borderY0 = border.y / textureHeight;
500 borderX1 = 1.0f - ( border.z / textureWidth );
501 borderY1 = 1.0f - ( border.w / textureHeight );
505 borderLeft = textureWidth * border.x;
506 borderTop = textureHeight * border.y;
507 borderRight = textureWidth * (1.0f - border.z);
508 borderBottom = textureHeight * (1.0f - border.w);
516 const float u0 = 0.0f;
517 const float u3 = 1.0f;
518 const float u1 = borderX0;
519 const float u2 = borderX1;
521 const float v0 = 0.0f;
522 const float v3 = 1.0f;
523 const float v1 = borderY0;
524 const float v2 = borderY1;
526 const float x0 = size.x * -0.5;
527 const float x1 = x0 + borderLeft;
528 const float x2 = x0 + size.x - borderRight;
529 const float x3 = x0 + size.x;
531 const float y0 = size.y * -0.5;
532 const float y1 = y0 + borderTop;
533 const float y2 = y0 + size.y - borderBottom;
534 const float y3 = y0 + size.y;
540 * We're breaking a quad in to 9 smaller quads, so that when it's
541 * stretched the corners maintain their size.
542 * For speed the 9-patch is drawn with a single triangle span, the draw
543 * order of the span is 1->9.
544 * Previously it would draw three separate spans (1->3, 4->6, 7->9), but now it
545 * it does it one go by turning the corner when gets to the end of each row.
547 * No indices are needed as we tell GL to render in strip mode
548 * (GL_TRIANGLE_STRIP), which is faster, and consumes less
551 * |---|---------------|---|
553 * |---|---------------|---|
558 * |---|---------------|---|
560 * |---|---------------|---|
563 Vertex2D vertsWithCenter[]={
614 const size_t vertsSize = sizeof( vertsWithCenter );
615 const unsigned int vertexCount = vertsSize / sizeof( vertsWithCenter[0] );
616 texture->MapUV( vertexCount, vertsWithCenter, pixelArea );
617 UpdateVertexBuffer( *mContextDELETEME, vertsSize, vertsWithCenter );
622 * The center part is not going to be rendered. The 9-patch border is drawn with
623 * a single triangle span, and the draw order of the span is 1->8.
625 * |---|---------------|---|
627 * |---|---------------|---|
631 * | | (not rendered)| |
633 * |---|---------------|---|
635 * |---|---------------|---|
638 Vertex2D vertsWithNoCenter[]={
653 // reset the starting point to x3, y1
676 // reset point to x0,y3
681 // top left box (starting from (x0,y3)) (7)
695 const size_t vertsSize = sizeof( vertsWithNoCenter );
696 const unsigned int vertexCount = vertsSize / sizeof( vertsWithNoCenter[0] );
697 texture->MapUV( vertexCount, vertsWithNoCenter, pixelArea );
698 UpdateVertexBuffer( *mContextDELETEME, vertsSize, vertsWithNoCenter );
700 // not using an index buffer
701 UpdateIndexBuffer( *mContextDELETEME, 0, NULL );
705 void ImageRenderer::SetGridMeshData( Texture* texture, const Vector2& size, const Vector4* border, bool borderInPixels, const PixelArea* pixelArea )
709 * In Grid Mode, we tessellate the single quad into smaller quads
710 * at approximately (guideGridSize x guideGridSize) in size.
712 * Conversion of Quad to Gridded Quad.
714 * |-----------| |---|---|---|
716 * | | -> |---|---|---|
718 * |-----------| |---|---|---|
721 * In Grid Mode, we tessellate each quad of a 9-patch
722 * (see SetNinePatchMeshData) into smaller quads at approximately
723 * (guideGridSize x guideGridSize) in size.
725 * This satisfies the two requirements of a 9-patch with grid:
727 * 1. Texture coordinates within each section of the 9-patch
728 * should change linearly to that 9-patch's rules.
729 * 2. The image as as whole should provide Vertex points at
730 * approximate guideGridSize intervals.
732 * The result should be the horizontal and vertical lines of
733 * a 9-patch overlayed by the horizontal and vertical lines of
737 * | | | | | | | <- Grid Markers
738 * -|-------|-------------------|-------|
742 * |-------+-------------------+-------|
747 * -| 4 | 5 | 6 | <- 9 Patch.
752 * |-------+-------------------+-------|
756 * -|-------|-------------------|-------|
760 * | | | | | | | <- Grid Markers
761 * -|-------|-------------------|-------|
764 * -|-----|-|---|-----|-----|---|-|-----|
765 * |-------+-------------------+-------|
767 * -|-----|-|---|-----|-----|---|-|-----|
770 * -|-----|-|---|-----|-----|---|-|-----| <- 9 Patch.
773 * -|-----|-|---|-----|-----|---|-|-----|
775 * |-------+-------------------+-------|
776 * -|-----|-|---|-----|-----|---|-|-----|
779 * -|-------|-------------------|-------|
782 std::vector<VertexToTextureCoord> horizontalDivisions;
783 std::vector<VertexToTextureCoord> verticalDivisions;
785 const float guideGridSize = mShader->GetGridDensity();
787 const int textureWidth = texture->GetWidth();
788 const int textureHeight = texture->GetHeight();
790 const float halfWidth = size.width * 0.5f;
791 const float halfHeight = size.height * 0.5f;
793 // Determine how many rectangles across and down to tesselate image into.
794 const int guideRectX = (size.width / guideGridSize);
795 const int guideRectY = (size.height / guideGridSize);
797 // Build up list of points in X axis where vertices need to go.
798 std::vector<VertexToTextureCoord> insertionList;
799 insertionList.reserve(4);
800 insertionList.push_back( VertexToTextureCoord( -halfWidth, 0.0f ) );
802 // If 9-Patch Border exists, add additional border points in list.
805 float borderX0, borderX1, borderU0, borderU1;
807 if ( borderInPixels )
809 borderX0 = border->x - halfWidth;
810 borderX1 = halfWidth - border->z;
812 borderU0 = border->x / textureWidth;
813 borderU1 = 1.0f - (border->z / textureWidth);
817 borderX0 = border->x * textureWidth - halfWidth;
818 borderX1 = halfWidth - (1.0f - border->z) * textureWidth;
819 borderU0 = border->x;
820 borderU1 = border->z;
823 insertionList.push_back( VertexToTextureCoord( borderX0, borderU0 ) );
824 insertionList.push_back( VertexToTextureCoord( borderX1, borderU1 ) );
827 insertionList.push_back( VertexToTextureCoord( halfWidth, 1.0f ) );
828 GenerateIntervals(horizontalDivisions, guideRectX + 2, insertionList);
830 // Build up list of points in Y axis where vertices need to go.
831 insertionList.clear();
832 insertionList.push_back( VertexToTextureCoord( -halfHeight, 0.0f ) );
834 // If 9-Patch Border exists, add additional border points in list.
837 float borderY0, borderY1, borderU0, borderU1;
839 if ( borderInPixels )
841 borderY0 = border->y - halfHeight;
842 borderY1 = halfHeight - border->w;
844 borderU0 = border->y / textureHeight;
845 borderU1 = 1.0f - (border->w / textureHeight);
849 borderY0 = border->y * textureHeight - halfHeight;
850 borderY1 = halfHeight - (1.0f - border->w) * textureHeight;
852 borderU0 = border->y;
853 borderU1 = border->w;
856 insertionList.push_back( VertexToTextureCoord( borderY0, borderU0 ) );
857 insertionList.push_back( VertexToTextureCoord( borderY1, borderU1 ) );
860 insertionList.push_back( VertexToTextureCoord( halfHeight, 1.0f ) );
861 GenerateIntervals(verticalDivisions, guideRectY + 2, insertionList);
863 // Now build up Vertex pattern based on the above X and Y lists.
864 const int totalVertices = horizontalDivisions.size() * verticalDivisions.size();
865 Vertex2D* vertices = new Vertex2D[totalVertices];
866 Vertex2D* vertex = vertices;
868 for(std::vector<VertexToTextureCoord>::const_iterator yIter = verticalDivisions.begin(); yIter != verticalDivisions.end(); ++yIter )
870 for(std::vector<VertexToTextureCoord>::const_iterator xIter = horizontalDivisions.begin(); xIter != horizontalDivisions.end(); ++xIter )
872 vertex->mX = xIter->x;
873 vertex->mU = xIter->u;
874 vertex->mY = yIter->x;
875 vertex->mV = yIter->u;
880 // Build up Triangle indicies, very predictable pattern.
881 const size_t rectX = horizontalDivisions.size() - 1;
882 const size_t rectY = verticalDivisions.size() - 1;
883 const size_t totalIndices = rectX * rectY * 6; // 2 triangles per quad (rect) and 3 points to define each triangle.
884 GLushort* indices = new GLushort[totalIndices];
886 GenerateMeshIndices(indices, rectX, rectY);
888 texture->MapUV( totalVertices, vertices, pixelArea );
890 UpdateVertexBuffer( *mContextDELETEME, totalVertices * sizeof(Vertex2D) , vertices );
891 UpdateIndexBuffer( *mContextDELETEME, totalIndices * sizeof(GLushort), indices );
897 void ImageRenderer::GenerateMeshIndices(GLushort* indices, int rectanglesX, int rectanglesY)
899 GLushort *i = indices;
900 const int meshEndIndex = rectanglesY * (rectanglesX + 1);
903 while(index < meshEndIndex)
905 const int rowEndIndex = index + rectanglesX;
906 for (; index < rowEndIndex; index++ )
909 *i++ = index + 1 + rectanglesX;
913 *i++ = index + 1 + rectanglesX;
914 *i++ = index + 2 + rectanglesX;
916 index++; // one extra vertex per row than rects.
920 ImageRenderer::ImageRenderer( NodeDataProvider& dataProvider )
921 : Renderer( dataProvider ),
923 mBorder( 0.45, 0.45, 0.1, 0.1 ),
927 mMeshType( ImageRenderer::QUAD ),
928 mIsMeshGenerated( false ),
929 mBorderInPixels( false ),
930 mUsePixelArea( false )
934 } // namespace SceneGraph
936 } // namespace Internal