diff -r 000000000000 -r 5d03bc08d59c graphicsdeviceinterface/directgdiadaptation/hwsrc/directgdidriverprocessstate.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graphicsdeviceinterface/directgdiadaptation/hwsrc/directgdidriverprocessstate.cpp Tue Feb 02 01:47:50 2010 +0200 @@ -0,0 +1,562 @@ +// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "directgdidriverprocessstate.h" + + +const TInt KMaxProcessStateHeapSize = 0x100000; + + +/** +Reports any error that occurs. +This is #defined so that we retain correct line information for the debug stream. +*/ +#define ProcessEglError(err) \ + { \ + const EGLint eglError = eglGetError(); \ + if (eglError == EGL_BAD_ALLOC) \ + err = KErrNoMemory; \ + else if (eglError == EGL_CONTEXT_LOST) \ + err = KErrDied; \ + else \ + err = KErrGeneral; \ + TBuf16<256> _message; \ + _message.Format(_L16("EGL Error: %x\n"), eglError); \ + GRAPHICS_LOGD_DEBUG(_message); \ + } +/** +Image source data constructor. +@param aSgImageId The unique identifier of the RSgImage used to create the EGLImage and VGImage. +@param aEglImage The EGLImage created from the RSgImage. +@param aVgImage The VGImage created from aEglImage. +@panic DGDIAdapter 48, if invalid images are passed in. +*/ +CImageSourceData::CImageSourceData(const TSgDrawableId& aSgImageId, EGLImageKHR aEglImage, VGImage aVgImage) : + iSgImageId(aSgImageId), + iEglImage(aEglImage), + iVgImage(aVgImage) + { + GRAPHICS_ASSERT_DEBUG(iEglImage != EGL_NO_IMAGE_KHR, EDirectGdiPanicImageSourceDataConstructorError); + GRAPHICS_ASSERT_DEBUG(iVgImage != VG_INVALID_HANDLE, EDirectGdiPanicImageSourceDataConstructorError); + } + +/** +Image source data destructor. +@panic DGDIAdapter 49, if reference count is not zero when this funtion is called. +*/ +CImageSourceData::~CImageSourceData() + { + GRAPHICS_ASSERT_ALWAYS(iRefCount == 0, EDirectGdiPanicImageSourceDataRefCountError); + } + +XDirectGdiDriverProcessState::XDirectGdiDriverProcessState() + { + iCreateMutexErr = iMutex.CreateLocal(); + } + + +/** +Creates a heap to store EGL and VG images. This heap will be shared between threads in +the current process. This is necessary as EGL images are shared between threads, and an attempt +to create an EGL image from a RSgImage in two different threads would fail if the EGL image was not +also shared. Also gets function pointers to image creation and destruction methods in EGL and saves +them for use later. Note that Initialize() must be called before you call CreateVgImage() or +DestroyVgImage(). + +@pre The client has called MutexCreationStatus() and checked for KErrNone. + +@param aDisplay The current EGL display handle + +@return KErrNone if successful, KErrNoMemory if we fail to create the heap, KErrGeneral +if we encounter an EGL error, otherwise one of the system-wide error codes. + */ +TInt XDirectGdiDriverProcessState::Initialize(EGLDisplay aDisplay) + { + iMutex.Wait(); + + // No need to re-initialize everything if we have already been initialized. + TInt err = KErrNone; + if (iLocalHeap == NULL) + { + // Create a heap that will be used to store EGLImages and VGImages that are created + // when the driver creates a source image (in CDirectGdiDriverImpl::CreateDrawableSource()) + iLocalHeap = UserHeap::ChunkHeap(NULL, 0, KMaxProcessStateHeapSize); + if (iLocalHeap == NULL) + { + err = KErrNoMemory; + } + } + + // Check the availability of the required EGL/VG extensions + if (err == KErrNone) + { + if (!iCheckedExtensionsAvailable) + { + err = CheckAllEglExtensions(aDisplay); + iCheckedExtensionsAvailable = ETrue; + +#ifdef _DEBUG_DIRECTGDI + RDebug::Printf("VG_VENDOR: %s\n", vgGetString(VG_VENDOR)); + RDebug::Printf("VG_RENDERER : %s\n", vgGetString(VG_RENDERER)); + RDebug::Printf("VG_VERSION : %s\n", vgGetString(VG_VERSION)); +#endif + } + } + + // Get function pointers for EGLImage creation and destruction and VGImage creation EGL functions. + // These need to be refreshed every time this process state is initialised as it cannot be guaranteed + // that the function pointers will remain the same after this process state was last closed + // (and EGL was terminated). + if (err == KErrNone) + { + iPfnEglCreateImageKHR = reinterpret_cast(eglGetProcAddress("eglCreateImageKHR")); + if (iPfnEglCreateImageKHR == NULL) + { + ProcessEglError(err); + err = KErrNotSupported; + } + } + + if (err == KErrNone) + { + iPfnEglDestroyImageKHR = reinterpret_cast(eglGetProcAddress("eglDestroyImageKHR")); + if (iPfnEglDestroyImageKHR == NULL) + { + ProcessEglError(err); + err = KErrNotSupported; + } + } + + if (err == KErrNone) + { + iPfnVgCreateImageTargetKHR = reinterpret_cast(eglGetProcAddress("vgCreateEGLImageTargetKHR")); + if (iPfnVgCreateImageTargetKHR == NULL) + { + ProcessEglError(err); + err = KErrNotSupported; + } + } + iMutex.Signal(); + + return err; + } + +/** +Destructor, asserts that the instance count is zero, and the count of images stored is zero. + +@panic DGDIAdapter 50, if the instance count is not zero (debug-only). +@panic DGDIAdapter 51, if the image count is not zero (debug-only). + */ +XDirectGdiDriverProcessState::~XDirectGdiDriverProcessState() + { + if (iLocalHeap != NULL) + { + iLocalHeap->Close(); + } + iMutex.Close(); + + GRAPHICS_ASSERT_DEBUG(iDriverInstanceCount == 0, EDirectGdiPanicProcessStateInstanceCountError); + GRAPHICS_ASSERT_DEBUG(iImages.Count() == 0, EDirectGdiPanicProcessStateImageCountError); + } + +/** +Creates a VGImage from the passed RSgImage. This method first creates a EGLImageKHR from +the RSgImage, from which it can create a VGImage. The EGLImageKHR and the VGImage are saved +so that they can be shared if a user attempts to create another VGImage from the same RSgImage. +If a VGImage has already been created from the passed RSgImage, the existing image is returned +instead of a new one being created. This will increment the reference count on that stored image. + +@param aDisplay The EGL display to associate the image with. +@param aVgImageRet A handle to the VGImage created is returned here. +@param aSgImage The VGImage will be created from this RSgImage. + +@pre Initialize() has been called. + +@return KErrNone if the image is created successfully, KErrGeneral if an EGL error occurs, +otherwise one of the system-wide error codes. + +@panic DGDIAdapter 33, if one of the member EGL function pointers is NULL (debug-only). +If this happens it may be that Initialize() has not been called for this process state. +@panic DGDIAdapter 36, if aDisplay is EGL_NO_DISPLAY (debug-only). + */ + TInt XDirectGdiDriverProcessState::CreateVgImage(EGLDisplay aDisplay, VGImage& aVgImageRet, const RSgImage& aSgImage) + { + VGImage vgImage = VG_INVALID_HANDLE; + TInt err = KErrNone; + aVgImageRet = VG_INVALID_HANDLE; + + iMutex.Wait(); + // If this image has already been created, just return a handle to the + // previously created one. + if (ImageExists(aSgImage, vgImage)) + { + aVgImageRet = vgImage; + iMutex.Signal(); + return KErrNone; + } + + // If the function pointers are NULL, Initialize() may not have been called + GRAPHICS_ASSERT_DEBUG(iPfnEglCreateImageKHR, EDirectGdiPanicProcessStateNotInitialized); + GRAPHICS_ASSERT_DEBUG(iPfnEglDestroyImageKHR, EDirectGdiPanicProcessStateNotInitialized); + GRAPHICS_ASSERT_DEBUG(iPfnVgCreateImageTargetKHR, EDirectGdiPanicProcessStateNotInitialized); + GRAPHICS_ASSERT_DEBUG(aDisplay != EGL_NO_DISPLAY, EDirectGdiPanicNoDisplay); + + // Create an EGLImage from the passed RSgImage, then create a VGImage from that and + // assign the VGImage to the newly created source. (The EGL image creation functions + // have already been retreived at this stage as function pointers using eglGetProcAddress() + // in InitialiseEgl()) + + // Pass in the preserved attribute. Without this the existing image data could be trashed. + EGLint createImageAttribs[] = {EGL_IMAGE_PRESERVED_SYMBIAN, EGL_TRUE, + EGL_NONE}; + + EGLImageKHR eglImage = iPfnEglCreateImageKHR(aDisplay, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR, reinterpret_cast(&aSgImage), createImageAttribs); + if (eglImage == EGL_NO_IMAGE_KHR) + { + ProcessEglError(err); + } + + if (err == KErrNone) + { + vgImage = iPfnVgCreateImageTargetKHR(eglImage); + if (vgImage == VG_INVALID_HANDLE) + { + ProcessEglError(err); + } + } + + if (err == KErrNone) + { + // Add the image to the array of images on the process heap + err = AddImage(aSgImage, eglImage, vgImage); + } + + if (err == KErrNone) + { + aVgImageRet = vgImage; + } + else + { + // Clean up + if (vgImage != VG_INVALID_HANDLE) + { + vgDestroyImage(vgImage); + } + iPfnEglDestroyImageKHR(aDisplay, eglImage); + } + + iMutex.Signal(); + + return err; + } + +/** +Destroys the passed VGImage. Searches for the image in the image array and +decrements its reference count if found. Destroys the image when the reference count +reaches zero. + +@param aDisplay The EGLDisplay the VGImage to be destroyed was created with. +@param aVgImage A handle to the VGImage to destroy. + +@return KErrNone if the image is destroyed successfully, otherwise one of the system-wide +error codes. + +@panic DGDIAdapter 33, if the EGL function pointer for destroying a VGImage is NULL (debug-only). + */ +TInt XDirectGdiDriverProcessState::DestroyVgImage(EGLDisplay aDisplay, VGImage aVgImage) + { + GRAPHICS_ASSERT_DEBUG(iPfnEglDestroyImageKHR, EDirectGdiPanicProcessStateNotInitialized); + + iMutex.Wait(); + TInt err = RemoveImage(aDisplay, aVgImage); + iMutex.Signal(); + + return err; + } + +/** +Increments the instance count. +*/ +void XDirectGdiDriverProcessState::OpenDriver() + { + iMutex.Wait(); + ++iDriverInstanceCount; + iMutex.Signal(); + } + +/** +Decrements the instance count. +If this is the last instance of a DirectGDI driver, the associated EGL display is terminated, and +there should be no images left in the array as they should all have been destroyed. + +@param aDriverEglDisplay An EGLDisplay associated with the driver being closed. If this is the last + driver being closed, the display is destroyed. +@panic DGDIAdapter 51, if the image count is not zero (debug-only). + */ +void XDirectGdiDriverProcessState::CloseDriver(EGLDisplay& aDriverEglDisplay) + { + iMutex.Wait(); + if (--iDriverInstanceCount == 0 && aDriverEglDisplay != EGL_NO_DISPLAY) + { + eglTerminate(aDriverEglDisplay); + aDriverEglDisplay = EGL_NO_DISPLAY; + // Set the function pointers to NULL as cannot be guaranteed that they will be recreated at the same address + // when EGL is next initialised. + iPfnEglCreateImageKHR = NULL; + iPfnEglDestroyImageKHR = NULL; + iPfnVgCreateImageTargetKHR = NULL; + GRAPHICS_ASSERT_DEBUG(iImages.Count() == 0, EDirectGdiPanicProcessStateImageCountError); + } + iMutex.Signal(); + } + +/** +Check that the following EGL extensions are available: + EGL_SYMBIAN_PIXMAP_TYPE_RSGIMAGE + EGL_KHR_image + EGL_SYMBIAN_image_preserved + +According to the spec, OpenVG extensions cannot be checked until a context has been made current, +which has not happened at this point, so currently there is no check for VG_KHR_EGL_image extension. + +@pre eglInitialize() must have been called for the passed EGLDisplay. + +@param aDisplay The current EGL display handle. + +@return KErrNone if all the extensions are available, KErrNotSupported if one or more +of the extensions are not supported by the EGL driver. + */ +TInt XDirectGdiDriverProcessState::CheckAllEglExtensions(EGLDisplay aDisplay) const + { + _LIT8(KEglRSgImage, "EGL_SYMBIAN_PIXMAP_TYPE_RSGIMAGE"); + _LIT8(KEglKhrImage, "EGL_KHR_image"); + _LIT8(KEglImagePreserved, "EGL_SYMBIAN_image_preserved"); + + TInt err = CheckForEglExtension(aDisplay, KEglRSgImage); + + if (err == KErrNone) + { + err = CheckForEglExtension(aDisplay, KEglKhrImage); + } + + if (err == KErrNone) + { + err = CheckForEglExtension(aDisplay, KEglImagePreserved); + } + + return err; + } + +/** +Checks whether the passed EGL extension string is available with this EGL driver. + +@param aDisplay The current EGL display handle. +@param aExtensionString The EGL extension string to search for. + +@return KErrNotSupported is the extension string is not found, KErrNone if it is found successfully. + */ +TInt XDirectGdiDriverProcessState::CheckForEglExtension(EGLDisplay aDisplay, + const TDesC8& aExtensionString) const +{ + const TText8* extensions = reinterpret_cast(eglQueryString(aDisplay, EGL_EXTENSIONS)); + + TBool found = EFalse; + if (extensions) + { + TPtrC8 ext(extensions); + if (ext.Find(aExtensionString) != KErrNotFound) + { + found = ETrue; + } + } + + return (found) ? KErrNone : KErrNotSupported; + } + +/** +Checks to see that the passed RSgImage exists in the image array. Increases +the reference count on the image if it is found. The calling method should be holding the mutex +iMutex open before calling this method. + +@param aSgImage The RSgImage to check for. +@param aVgImageRet The handle of the VGImage found is returned here. + +@pre The mutex iMutex is held. + +@return ETrue if the image was found, EFalse otherwise. + +@panic DGDIAdapter 52, if the mutex has not been opened before calling this method. + This panic only happens when _DEBUG_DIRECTGDI is defined. + */ +TBool XDirectGdiDriverProcessState::ImageExists(const RSgImage& aSgImage, VGImage& aVgImageRet) + { +#ifdef _DEBUG_DIRECTGDI + GRAPHICS_ASSERT_ALWAYS(iMutex.IsHeld(), EDirectGdiPanicImageMutexError); +#endif + aVgImageRet = VG_INVALID_HANDLE; + + CImageSourceData* imageData = FindImage(aSgImage); + if (imageData) + { + aVgImageRet = imageData->VgImage(); + imageData->Open(); + } + + return (aVgImageRet != VG_INVALID_HANDLE); + } + +/** +Searches for an image in the array of images using the passed RSgImage id to find it. +The calling method should be holding the images mutex iMutex open before calling this method. + +@param aSgImage The RSgImage to search for. + +@pre The mutex iMutex is held. + +@return Pointer to the CImageSourceData object held in the images array for the passed +RSgImage if it is found, NULL if the image is not found. + +@panic DGDIAdapter 52, if the mutex has not been opened before calling this method. + This panic only happens when _DEBUG_DIRECTGDI is defined. + */ +CImageSourceData* XDirectGdiDriverProcessState::FindImage(const RSgImage& aSgImage) + { +#ifdef _DEBUG_DIRECTGDI + GRAPHICS_ASSERT_ALWAYS(iMutex.IsHeld(), EDirectGdiPanicImageMutexError); +#endif + CImageSourceData* imageDataRet = NULL; + + for (TInt i = 0; i < iImages.Count() && !imageDataRet; i++) + { + CImageSourceData* imageData = reinterpret_cast(iImages[i]); + if (imageData->SgImageId() == aSgImage.Id()) + { + imageDataRet = imageData; + } + } + + return imageDataRet; + } + +/** +Adds an image to the images array by saving its RSgImage id, its EGLImageKHR handle and its VGImage +handle. A new CImageSourceData object is created to hold this information. This CImageSourceData object +is also used to maintain a reference count on the number of times this image has been shared. +The calling method should be holding the images mutex iMutex open before calling this method. + +@see RemoveImage() + +@param aSgImage The RSgImage to store, only the RSgImage id will be stored. +@param aEglImage The EGLImageKHR to store. +@param aVgImage The VGImage to stord. + +@pre The mutex iMutex is held. +@return KErrNone if the details of the passed image are added to the image array successfully, KErrNoMemory +if memory allocation fails, otherwise one of the system-wide error codes. + +@panic DGDIAdapter 52, if the mutex has not been opened before calling this method. + This panic only happens when _DEBUG_DIRECTGDI is defined. + */ +TInt XDirectGdiDriverProcessState::AddImage(const RSgImage& aSgImage, EGLImageKHR aEglImage, VGImage aVgImage) + { +#ifdef _DEBUG_DIRECTGDI + GRAPHICS_ASSERT_ALWAYS(iMutex.IsHeld(), EDirectGdiPanicImageMutexError); +#endif + TInt err = KErrNone; + RHeap *prevHeap = User::SwitchHeap(iLocalHeap); + + CImageSourceData* imageData = new CImageSourceData(aSgImage.Id(), aEglImage, aVgImage); + if (imageData == NULL) + { + err = KErrNoMemory; + } + if (err == KErrNone) + { + err = iImages.InsertInAddressOrder(imageData); + } + if (err == KErrNone) + { + imageData->Open(); + } + else + { + delete imageData; + } + + User::SwitchHeap(prevHeap); + return err; + } + +/** +Removes the passed image from the image array by searching for it using its VGImage handle. +If the image is found in the image array, its reference count will be decremented. When the count reaches +zero the image will be removed from the image array and destroyed. + +@see AddImage() + +@param aDisplay The EGL display this image was created with. +@param aVgImage The hande to a VGImage to be removed from the image array. + +@pre The mutex iMutex is held. + +@return KErrNone if the image is removed from the image array successfully, KErrNotFound if the image +cannot be found in the image array, otherwise one of the system-wide error codes. + +@panic DGDIAdapter 52, if the mutex has not been opened before calling this method. + This panic only happens when _DEBUG_DIRECTGDI is defined. + */ +TInt XDirectGdiDriverProcessState::RemoveImage(EGLDisplay aDisplay, VGImage aVgImage) + { +#ifdef _DEBUG_DIRECTGDI + GRAPHICS_ASSERT_ALWAYS(iMutex.IsHeld(), EDirectGdiPanicImageMutexError); +#endif + TInt err = KErrNone; + TInt index = -1; + CImageSourceData* removeImage = NULL; + + for (TInt i = 0; i < iImages.Count() && (index == -1); i++) + { + if (CImageSourceData* imageData = reinterpret_cast(iImages[i])) + { + if (imageData->VgImage() == aVgImage) + { + // Only remove the image if the reference count is zero + if (imageData->Close() == 0) + removeImage = imageData; + index = i; + } + } + } + + if (removeImage) + { + // Clean up the EGL and VG images + vgDestroyImage(removeImage->VgImage()); + iPfnEglDestroyImageKHR(aDisplay, removeImage->EglImage()); + + RHeap *prevHeap = User::SwitchHeap(iLocalHeap); + iImages.Remove(index); + iImages.GranularCompress(); + delete removeImage; + User::SwitchHeap(prevHeap); + } + + if (index == -1) + { + err = KErrNotFound; + } + + return err; + }