upload android base code part4

This commit is contained in:
August 2018-08-08 17:00:29 +08:00
parent b9e30e05b1
commit 78ea2404cd
23455 changed files with 5250148 additions and 0 deletions

View file

@ -0,0 +1,28 @@
LOCAL_PATH := $(call my-dir)
include $(LOCAL_PATH)/../common.mk
include $(CLEAR_VARS)
LOCAL_VENDOR_MODULE := true
LOCAL_COPY_HEADERS_TO := $(common_header_export_path)
LOCAL_COPY_HEADERS := TonemapFactory.h Tonemapper.h
LOCAL_VENDOR_MODULE := true
include $(BUILD_COPY_HEADERS)
include $(CLEAR_VARS)
LOCAL_MODULE := libgpu_tonemapper
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
LOCAL_C_INCLUDES := $(TARGET_OUT_HEADERS)/qcom/display/
LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
LOCAL_SHARED_LIBRARIES := libEGL libGLESv2 libGLESv3 libui libutils liblog
LOCAL_CFLAGS := $(version_flag) -Wno-missing-field-initializers -Wall \
-Wno-unused-parameter -std=c++11 -DLOG_TAG=\"GPU_TONEMAPPER\"
LOCAL_SRC_FILES := TonemapFactory.cpp \
glengine.cpp \
EGLImageBuffer.cpp \
EGLImageWrapper.cpp \
Tonemapper.cpp
include $(BUILD_SHARED_LIBRARY)

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "EGLImageBuffer.h"
#include <cutils/native_handle.h>
#include <gralloc_priv.h>
#include <ui/GraphicBuffer.h>
#include <map>
#include "EGLImageWrapper.h"
#include "glengine.h"
//-----------------------------------------------------------------------------
EGLImageKHR create_eglImage(android::sp<android::GraphicBuffer> graphicBuffer)
//-----------------------------------------------------------------------------
{
bool isProtected = (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
isProtected ? EGL_TRUE : EGL_NONE, EGL_NONE};
EGLImageKHR eglImage = eglCreateImageKHR(
eglGetCurrentDisplay(), (EGLContext)EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
(EGLClientBuffer)(graphicBuffer->getNativeBuffer()), attrs);
return eglImage;
}
//-----------------------------------------------------------------------------
EGLImageBuffer::EGLImageBuffer(android::sp<android::GraphicBuffer> graphicBuffer)
//-----------------------------------------------------------------------------
{
// this->graphicBuffer = graphicBuffer;
this->eglImageID = create_eglImage(graphicBuffer);
this->width = graphicBuffer->getWidth();
this->height = graphicBuffer->getHeight();
textureID = 0;
renderbufferID = 0;
framebufferID = 0;
}
//-----------------------------------------------------------------------------
EGLImageBuffer::~EGLImageBuffer()
//-----------------------------------------------------------------------------
{
if (textureID != 0) {
GL(glDeleteTextures(1, &textureID));
textureID = 0;
}
if (renderbufferID != 0) {
GL(glDeleteRenderbuffers(1, &renderbufferID));
renderbufferID = 0;
}
if (framebufferID != 0) {
GL(glDeleteFramebuffers(1, &framebufferID));
framebufferID = 0;
}
// Delete the eglImage
if (eglImageID != 0)
{
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImageID);
eglImageID = 0;
}
}
//-----------------------------------------------------------------------------
int EGLImageBuffer::getWidth()
//-----------------------------------------------------------------------------
{
return width;
}
//-----------------------------------------------------------------------------
int EGLImageBuffer::getHeight()
//-----------------------------------------------------------------------------
{
return height;
}
//-----------------------------------------------------------------------------
unsigned int EGLImageBuffer::getTexture()
//-----------------------------------------------------------------------------
{
if (textureID == 0) {
bindAsTexture();
}
return textureID;
}
//-----------------------------------------------------------------------------
unsigned int EGLImageBuffer::getFramebuffer()
//-----------------------------------------------------------------------------
{
if (framebufferID == 0) {
bindAsFramebuffer();
}
return framebufferID;
}
//-----------------------------------------------------------------------------
void EGLImageBuffer::bindAsTexture()
//-----------------------------------------------------------------------------
{
if (textureID == 0) {
GL(glGenTextures(1, &textureID));
int target = 0x8D65;
GL(glBindTexture(target, textureID));
GL(glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL(glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL(glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
GL(glEGLImageTargetTexture2DOES(0x8D65, eglImageID));
}
GL(glBindTexture(0x8D65, textureID));
}
//-----------------------------------------------------------------------------
void EGLImageBuffer::bindAsFramebuffer()
//-----------------------------------------------------------------------------
{
if (renderbufferID == 0) {
GL(glGenFramebuffers(1, &framebufferID));
GL(glGenRenderbuffers(1, &renderbufferID));
GL(glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID));
GL(glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, eglImageID));
GL(glBindFramebuffer(GL_FRAMEBUFFER, framebufferID));
GL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
renderbufferID));
GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (result != GL_FRAMEBUFFER_COMPLETE) {
ALOGI("%s Framebuffer Invalid***************", __FUNCTION__);
}
}
GL(glBindFramebuffer(GL_FRAMEBUFFER, framebufferID));
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __EGLIMAGE_BUFFER_H__
#define __EGLIMAGE_BUFFER_H__
#include <cutils/native_handle.h>
#include <gralloc_priv.h>
#include <ui/GraphicBuffer.h>
#include "engine.h"
class EGLImageBuffer {
// android::sp<android::GraphicBuffer> graphicBuffer;
void *eglImageID;
int width;
int height;
uint textureID;
uint renderbufferID;
uint framebufferID;
public:
int getWidth();
int getHeight();
EGLImageBuffer(android::sp<android::GraphicBuffer>);
unsigned int getTexture();
unsigned int getFramebuffer();
void bindAsTexture();
void bindAsFramebuffer();
~EGLImageBuffer();
static EGLImageBuffer *from(const private_handle_t *src);
static void clear();
};
#endif //__EGLIMAGE_BUFFER_H__

View file

@ -0,0 +1,154 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "EGLImageWrapper.h"
#include <cutils/native_handle.h>
#include <gralloc_priv.h>
#include <ui/GraphicBuffer.h>
#include <fcntl.h>
#include <linux/msm_ion.h>
//-----------------------------------------------------------------------------
void free_ion_cookie(int ion_fd, int cookie)
//-----------------------------------------------------------------------------
{
if (ion_fd && !ioctl(ion_fd, ION_IOC_FREE, &cookie)) {
} else {
ALOGE("ION_IOC_FREE failed: ion_fd = %d, cookie = %d", ion_fd, cookie);
}
}
//-----------------------------------------------------------------------------
int get_ion_cookie(int ion_fd, int fd)
//-----------------------------------------------------------------------------
{
int cookie = fd;
struct ion_fd_data fdData;
memset(&fdData, 0, sizeof(fdData));
fdData.fd = fd;
if (ion_fd && !ioctl(ion_fd, ION_IOC_IMPORT, &fdData)) {
cookie = fdData.handle;
} else {
ALOGE("ION_IOC_IMPORT failed: ion_fd = %d, fd = %d", ion_fd, fd);
}
return cookie;
}
//-----------------------------------------------------------------------------
EGLImageWrapper::DeleteEGLImageCallback::DeleteEGLImageCallback(int fd)
//-----------------------------------------------------------------------------
{
ion_fd = fd;
}
//-----------------------------------------------------------------------------
void EGLImageWrapper::DeleteEGLImageCallback::operator()(int& k, EGLImageBuffer*& eglImage)
//-----------------------------------------------------------------------------
{
free_ion_cookie(ion_fd, k);
if( eglImage != 0 )
{
delete eglImage;
}
}
//-----------------------------------------------------------------------------
EGLImageWrapper::EGLImageWrapper()
//-----------------------------------------------------------------------------
{
eglImageBufferMap = new android::LruCache<int, EGLImageBuffer*>(32);
ion_fd = open("/dev/ion", O_RDONLY);
callback = new DeleteEGLImageCallback(ion_fd);
eglImageBufferMap->setOnEntryRemovedListener(callback);
}
//-----------------------------------------------------------------------------
EGLImageWrapper::~EGLImageWrapper()
//-----------------------------------------------------------------------------
{
if( eglImageBufferMap != 0 )
{
eglImageBufferMap->clear();
delete eglImageBufferMap;
eglImageBufferMap = 0;
}
if( callback != 0 )
{
delete callback;
callback = 0;
}
if( ion_fd > 0 )
{
close(ion_fd);
}
ion_fd = -1;
}
//-----------------------------------------------------------------------------
static EGLImageBuffer* L_wrap(const private_handle_t *src)
//-----------------------------------------------------------------------------
{
EGLImageBuffer* result = 0;
native_handle_t *native_handle = const_cast<private_handle_t *>(src);
int flags = android::GraphicBuffer::USAGE_HW_TEXTURE |
android::GraphicBuffer::USAGE_SW_READ_NEVER |
android::GraphicBuffer::USAGE_SW_WRITE_NEVER;
if (src->flags & private_handle_t::PRIV_FLAGS_SECURE_BUFFER) {
flags |= android::GraphicBuffer::USAGE_PROTECTED;
}
android::sp<android::GraphicBuffer> graphicBuffer =
new android::GraphicBuffer(src->unaligned_width, src->unaligned_height, src->format,
#ifndef __NOUGAT__
1, // Layer count
#endif
flags, src->width /*src->stride*/,
native_handle, false);
result = new EGLImageBuffer(graphicBuffer);
return result;
}
//-----------------------------------------------------------------------------
EGLImageBuffer *EGLImageWrapper::wrap(const void *pvt_handle)
//-----------------------------------------------------------------------------
{
const private_handle_t *src = static_cast<const private_handle_t *>(pvt_handle);
int ion_cookie = get_ion_cookie(ion_fd, src->fd);
EGLImageBuffer* eglImage = eglImageBufferMap->get(ion_cookie);
if( eglImage == 0 )
{
eglImage = L_wrap(src);
eglImageBufferMap->put(ion_cookie, eglImage);
}
else {
free_ion_cookie(ion_fd, ion_cookie);
}
return eglImage;
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __TONEMAPPER_EGLIMAGEWRAPPER_H__
#define __TONEMAPPER_EGLIMAGEWRAPPER_H__
#include <utils/LruCache.h>
#include "EGLImageBuffer.h"
class EGLImageWrapper {
private:
class DeleteEGLImageCallback : public android::OnEntryRemoved<int, EGLImageBuffer*>
{
private:
int ion_fd;
public:
DeleteEGLImageCallback(int ion_fd);
void operator()(int& ion_cookie, EGLImageBuffer*& eglImage);
};
android::LruCache<int, EGLImageBuffer *>* eglImageBufferMap;
DeleteEGLImageCallback* callback;
int ion_fd;
public:
EGLImageWrapper();
~EGLImageWrapper();
EGLImageBuffer* wrap(const void *pvt_handle);
};
#endif //__TONEMAPPER_EGLIMAGEWRAPPER_H__

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "TonemapFactory.h"
#include <utils/Log.h>
#include "Tonemapper.h"
#include "engine.h"
//----------------------------------------------------------------------------------------------------------------------------------------------------------
Tonemapper *TonemapperFactory_GetInstance(int type, void *colorMap, int colorMapSize,
void *lutXform, int lutXformSize, bool isSecure)
//----------------------------------------------------------------------------------------------------------------------------------------------------------
{
// build the tonemapper
Tonemapper *tonemapper = Tonemapper::build(type, colorMap, colorMapSize, lutXform, lutXformSize, isSecure);
return tonemapper;
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __TONEMAPPER_TONEMAPPERFACTORY_H__
#define __TONEMAPPER_TONEMAPPERFACTORY_H__
#include "Tonemapper.h"
#ifdef __cplusplus
extern "C" {
#endif
// returns an instance of Tonemapper
Tonemapper *TonemapperFactory_GetInstance(int type, void *colorMap, int colorMapSize,
void *lutXform, int lutXformSize, bool isSecure);
#ifdef __cplusplus
}
#endif
#endif //__TONEMAPPER_TONEMAPPERFACTORY_H__

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <utils/Log.h>
#include "EGLImageWrapper.h"
#include "Tonemapper.h"
#include "engine.h"
#include "forward_tonemap.inl"
#include "fullscreen_vertex_shader.inl"
#include "rgba_inverse_tonemap.inl"
//-----------------------------------------------------------------------------
Tonemapper::Tonemapper()
//-----------------------------------------------------------------------------
{
tonemapTexture = 0;
lutXformTexture = 0;
programID = 0;
eglImageWrapper = new EGLImageWrapper();
lutXformScaleOffset[0] = 1.0f;
lutXformScaleOffset[1] = 0.0f;
tonemapScaleOffset[0] = 1.0f;
tonemapScaleOffset[1] = 0.0f;
}
//-----------------------------------------------------------------------------
Tonemapper::~Tonemapper()
//-----------------------------------------------------------------------------
{
void* caller_context = engine_backup();
engine_bind(engineContext);
engine_deleteInputBuffer(tonemapTexture);
engine_deleteInputBuffer(lutXformTexture);
engine_deleteProgram(programID);
// clear EGLImage mappings
if (eglImageWrapper != 0) {
delete eglImageWrapper;
eglImageWrapper = 0;
}
engine_shutdown(engineContext);
// restore the caller context
engine_bind(caller_context);
engine_free_backup(caller_context);
}
//-----------------------------------------------------------------------------
Tonemapper *Tonemapper::build(int type, void *colorMap, int colorMapSize, void *lutXform,
int lutXformSize, bool isSecure)
//-----------------------------------------------------------------------------
{
if (colorMapSize <= 0) {
ALOGE("Invalid Color Map size = %d", colorMapSize);
return NULL;
}
// build new tonemapper
Tonemapper *tonemapper = new Tonemapper();
tonemapper->engineContext = engine_initialize(isSecure);
void* caller_context = engine_backup();
engine_bind(tonemapper->engineContext);
// load the 3d lut
tonemapper->tonemapTexture = engine_load3DTexture(colorMap, colorMapSize, 0);
tonemapper->tonemapScaleOffset[0] = ((float)(colorMapSize-1))/((float)(colorMapSize));
tonemapper->tonemapScaleOffset[1] = 1.0f/(2.0f*colorMapSize);
// load the non-uniform xform
tonemapper->lutXformTexture = engine_load1DTexture(lutXform, lutXformSize, 0);
bool bUseXform = (tonemapper->lutXformTexture != 0) && (lutXformSize != 0);
if( bUseXform )
{
tonemapper->lutXformScaleOffset[0] = ((float)(lutXformSize-1))/((float)(lutXformSize));
tonemapper->lutXformScaleOffset[1] = 1.0f/(2.0f*lutXformSize);
}
// create the program
const char *fragmentShaders[3];
int fragmentShaderCount = 0;
const char *version = "#version 300 es\n";
const char *define = "#define USE_NONUNIFORM_SAMPLING\n";
fragmentShaders[fragmentShaderCount++] = version;
// non-uniform sampling
if (bUseXform) {
fragmentShaders[fragmentShaderCount++] = define;
}
if (type == TONEMAP_INVERSE) { // inverse tonemapping
fragmentShaders[fragmentShaderCount++] = rgba_inverse_tonemap_shader;
} else { // forward tonemapping
fragmentShaders[fragmentShaderCount++] = forward_tonemap_shader;
}
tonemapper->programID =
engine_loadProgram(1, &fullscreen_vertex_shader, fragmentShaderCount, fragmentShaders);
// restore the caller context
engine_bind(caller_context);
engine_free_backup(caller_context);
return tonemapper;
}
//-----------------------------------------------------------------------------
int Tonemapper::blit(const void *dst, const void *src, int srcFenceFd)
//-----------------------------------------------------------------------------
{
void* caller_context = engine_backup();
// make current
engine_bind(engineContext);
// create eglimages if required
EGLImageBuffer *dst_buffer = eglImageWrapper->wrap(dst);
EGLImageBuffer *src_buffer = eglImageWrapper->wrap(src);
// bind the program
engine_setProgram(programID);
engine_setData2f(3, tonemapScaleOffset);
bool bUseXform = (lutXformTexture != 0);
if( bUseXform )
{
engine_setData2f(4, lutXformScaleOffset);
}
// set destination
engine_setDestination(dst_buffer->getFramebuffer(), 0, 0, dst_buffer->getWidth(),
dst_buffer->getHeight());
// set source
engine_setExternalInputBuffer(0, src_buffer->getTexture());
// set 3d lut
engine_set3DInputBuffer(1, tonemapTexture);
// set non-uniform xform
engine_set2DInputBuffer(2, lutXformTexture);
// perform
int fenceFD = engine_blit(srcFenceFd);
// restore the caller context
engine_bind(caller_context);
engine_free_backup(caller_context);
return fenceFD;
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __TONEMAPPER_TONEMAP_H__
#define __TONEMAPPER_TONEMAP_H__
#define TONEMAP_FORWARD 0
#define TONEMAP_INVERSE 1
#include "EGLImageWrapper.h"
#include "engine.h"
class Tonemapper {
private:
void* engineContext;
unsigned int tonemapTexture;
unsigned int lutXformTexture;
unsigned int programID;
float lutXformScaleOffset[2];
float tonemapScaleOffset[2];
EGLImageWrapper* eglImageWrapper;
Tonemapper();
public:
~Tonemapper();
static Tonemapper *build(int type, void *colorMap, int colorMapSize, void *lutXform,
int lutXformSize, bool isSecure);
int blit(const void *dst, const void *src, int srcFenceFd);
};
#endif //__TONEMAPPER_TONEMAP_H__

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __TONEMAPPER_ENGINE_H__
#define __TONEMAPPER_ENGINE_H__
void* engine_initialize(bool isSecure);
void engine_bind(void*);
void* engine_backup();
void engine_free_backup(void*);
void engine_shutdown(void*);
unsigned int engine_loadProgram(int, const char **, int, const char **);
void engine_setProgram(int);
void engine_deleteProgram(unsigned int);
unsigned int engine_load3DTexture(void *data, int sz, int format);
unsigned int engine_load1DTexture(void *xform, int xformSize, int format);
void engine_deleteInputBuffer(unsigned int);
void engine_set2DInputBuffer(int binding, unsigned int textureID);
void engine_set3DInputBuffer(int binding, unsigned int textureID);
void engine_setExternalInputBuffer(int binding, unsigned int textureID);
void engine_setDestination(int id, int x, int y, int w, int h);
void engine_setData2f(int loc, float* data);
int engine_blit(int);
#endif //__TONEMAPPER_ENGINE_H__

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const char* forward_tonemap_shader = ""
"#extension GL_OES_EGL_image_external_essl3 : require \n"
"precision highp float; \n"
"precision highp sampler2D; \n"
"layout(binding = 0) uniform samplerExternalOES externalTexture; \n"
"layout(binding = 1) uniform sampler3D tonemapper; \n"
"layout(binding = 2) uniform sampler2D xform; \n"
"layout(location = 3) uniform vec2 tSO; \n"
"#ifdef USE_NONUNIFORM_SAMPLING \n"
"layout(location = 4) uniform vec2 xSO; \n"
"#endif \n"
"in vec2 uv; \n"
"out vec4 fs_color; \n"
" \n"
"vec3 ScaleOffset(in vec3 samplePt, in vec2 so) \n"
"{ \n"
" vec3 adjPt = so.x * samplePt + so.y; \n"
" return adjPt; \n"
"} \n"
" \n"
"void main() \n"
"{ \n"
"vec2 flipped = vec2(uv.x, 1.0f - uv.y); \n"
"vec4 rgb = texture(externalTexture, flipped); \n"
"#ifdef USE_NONUNIFORM_SAMPLING \n"
"vec3 adj = ScaleOffset(rgb.xyz, xSO); \n"
"float r = texture(xform, vec2(adj.r, 0.5f)).r; \n"
"float g = texture(xform, vec2(adj.g, 0.5f)).g; \n"
"float b = texture(xform, vec2(adj.b, 0.5f)).b; \n"
"#else \n"
"float r = rgb.r; \n"
"float g = rgb.g; \n"
"float b = rgb.b; \n"
"#endif \n"
"fs_color.rgb = texture(tonemapper, ScaleOffset(vec3(r, g, b), tSO)).rgb; \n"
"} \n";

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const char* fullscreen_vertex_shader = " "
"#version 300 es \n"
"precision highp float; \n"
"layout(location = 0) in vec2 iUV; \n"
"out vec2 uv; \n"
"void main() \n"
"{ \n"
" vec2 positions[3]; \n"
" positions[0] = vec2(-1.0f, 3.0f); \n"
" positions[1] = vec2(-1.0f, -1.0f); \n"
" positions[2] = vec2(3.0f, -1.0f); \n"
" vec2 uvs[3]; \n"
" uvs[0] = vec2(0.0f, -1.0f); \n"
" uvs[1] = vec2(0.0f, 1.0f); \n"
" uvs[2] = vec2(2.0f, 1.0f); \n"
" gl_Position = vec4(positions[gl_VertexID], -1.0f, 1.0f); \n"
" uv = uvs[gl_VertexID]; \n"
"} \n";

View file

@ -0,0 +1,436 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "glengine.h"
#include <utils/Log.h>
#include "engine.h"
void checkGlError(const char *, int);
void checkEglError(const char *, int);
class EngineContext {
public:
EGLDisplay eglDisplay;
EGLContext eglContext;
EGLSurface eglSurface;
EngineContext()
{
eglDisplay = EGL_NO_DISPLAY;
eglContext = EGL_NO_CONTEXT;
eglSurface = EGL_NO_SURFACE;
}
};
//-----------------------------------------------------------------------------
// Make Current
void engine_bind(void* context)
//-----------------------------------------------------------------------------
{
EngineContext* engineContext = (EngineContext*)(context);
EGL(eglMakeCurrent(engineContext->eglDisplay, engineContext->eglSurface, engineContext->eglSurface, engineContext->eglContext));
}
//-----------------------------------------------------------------------------
// store the current context(caller)
void* engine_backup()
{
EngineContext* callerContext = new EngineContext();
// store the previous display/context
callerContext->eglDisplay = eglGetCurrentDisplay();
callerContext->eglContext = eglGetCurrentContext();
callerContext->eglSurface = eglGetCurrentSurface(EGL_DRAW);
return (void*)callerContext;
}
//-----------------------------------------------------------------------------
// frees the backed up caller context
void engine_free_backup(void* context)
{
EngineContext* callerContext = (EngineContext*)(context);
delete callerContext;
}
//-----------------------------------------------------------------------------
// initialize GL
//
void* engine_initialize(bool isSecure)
//-----------------------------------------------------------------------------
{
EngineContext* engineContext = new EngineContext();
// display
engineContext->eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
EGL(eglBindAPI(EGL_OPENGL_ES_API));
// initialize
EGL(eglInitialize(engineContext->eglDisplay, 0, 0));
// config
EGLConfig eglConfig;
EGLint eglConfigAttribList[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_NONE};
int numConfig = 0;
EGL(eglChooseConfig(engineContext->eglDisplay, eglConfigAttribList, &eglConfig, 1, &numConfig));
// context
EGLint eglContextAttribList[] = {EGL_CONTEXT_CLIENT_VERSION, 3,
isSecure ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
isSecure ? EGL_TRUE : EGL_NONE,
EGL_NONE};
engineContext->eglContext = eglCreateContext(engineContext->eglDisplay, eglConfig, NULL, eglContextAttribList);
// surface
EGLint eglSurfaceAttribList[] = {EGL_WIDTH, 1,
EGL_HEIGHT, 1,
isSecure ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
isSecure ? EGL_TRUE : EGL_NONE,
EGL_NONE};
engineContext->eglSurface = eglCreatePbufferSurface(engineContext->eglDisplay, eglConfig, eglSurfaceAttribList);
eglMakeCurrent(engineContext->eglDisplay, engineContext->eglSurface, engineContext->eglSurface, engineContext->eglContext);
ALOGI("In %s context = %p", __FUNCTION__, (void *)(engineContext->eglContext));
return (void*)(engineContext);
}
//-----------------------------------------------------------------------------
// Shutdown.
void engine_shutdown(void* context)
//-----------------------------------------------------------------------------
{
EngineContext* engineContext = (EngineContext*)context;
EGL(eglMakeCurrent(engineContext->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
EGL(eglDestroySurface(engineContext->eglDisplay, engineContext->eglSurface));
EGL(eglDestroyContext(engineContext->eglDisplay, engineContext->eglContext));
EGL(eglTerminate(engineContext->eglDisplay));
engineContext->eglDisplay = EGL_NO_DISPLAY;
engineContext->eglContext = EGL_NO_CONTEXT;
engineContext->eglSurface = EGL_NO_SURFACE;
}
//-----------------------------------------------------------------------------
void engine_deleteInputBuffer(unsigned int id)
//-----------------------------------------------------------------------------
{
if (id != 0) {
GL(glDeleteTextures(1, &id));
}
}
//-----------------------------------------------------------------------------
void engine_deleteProgram(unsigned int id)
//-----------------------------------------------------------------------------
{
if (id != 0) {
GL(glDeleteProgram(id));
}
}
//-----------------------------------------------------------------------------
void engine_setData2f(int location, float* data)
//-----------------------------------------------------------------------------
{
GL(glUniform2f(location, data[0], data[1]));
}
//-----------------------------------------------------------------------------
unsigned int engine_load3DTexture(void *colorMapData, int sz, int format)
//-----------------------------------------------------------------------------
{
GLuint texture = 0;
GL(glGenTextures(1, &texture));
GL(glBindTexture(GL_TEXTURE_3D, texture));
GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL(glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL(glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB10_A2, sz, sz, sz, 0, GL_RGBA,
GL_UNSIGNED_INT_2_10_10_10_REV, colorMapData));
return texture;
}
//-----------------------------------------------------------------------------
unsigned int engine_load1DTexture(void *data, int sz, int format)
//-----------------------------------------------------------------------------
{
GLuint texture = 0;
if ((data != 0) && (sz != 0)) {
GL(glGenTextures(1, &texture));
GL(glBindTexture(GL_TEXTURE_2D, texture));
GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB10_A2, sz, 1, 0, GL_RGBA,
GL_UNSIGNED_INT_2_10_10_10_REV, data));
}
return texture;
}
//-----------------------------------------------------------------------------
void dumpShaderLog(int shader)
//-----------------------------------------------------------------------------
{
int success = 0;
GLchar infoLog[512];
GL(glGetShaderiv(shader, GL_COMPILE_STATUS, &success));
if (!success) {
glGetShaderInfoLog(shader, 512, NULL, infoLog);
ALOGI("Shader Failed to compile: %s\n", infoLog);
}
}
//-----------------------------------------------------------------------------
GLuint engine_loadProgram(int vertexEntries, const char **vertex, int fragmentEntries,
const char **fragment)
//-----------------------------------------------------------------------------
{
GLuint progId = glCreateProgram();
int vertId = glCreateShader(GL_VERTEX_SHADER);
int fragId = glCreateShader(GL_FRAGMENT_SHADER);
GL(glShaderSource(vertId, vertexEntries, vertex, 0));
GL(glCompileShader(vertId));
dumpShaderLog(vertId);
GL(glShaderSource(fragId, fragmentEntries, fragment, 0));
GL(glCompileShader(fragId));
dumpShaderLog(fragId);
GL(glAttachShader(progId, vertId));
GL(glAttachShader(progId, fragId));
GL(glLinkProgram(progId));
GL(glDetachShader(progId, vertId));
GL(glDetachShader(progId, fragId));
GL(glDeleteShader(vertId));
GL(glDeleteShader(fragId));
return progId;
}
//-----------------------------------------------------------------------------
void WaitOnNativeFence(int fd)
//-----------------------------------------------------------------------------
{
if (fd != -1) {
EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fd, EGL_NONE};
EGLSyncKHR sync = eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
if (sync == EGL_NO_SYNC_KHR) {
ALOGE("%s - Failed to Create sync from source fd", __FUNCTION__);
} else {
// the gpu will wait for this sync - not this cpu thread.
EGL(eglWaitSyncKHR(eglGetCurrentDisplay(), sync, 0));
EGL(eglDestroySyncKHR(eglGetCurrentDisplay(), sync));
}
}
}
//-----------------------------------------------------------------------------
int CreateNativeFence()
//-----------------------------------------------------------------------------
{
int fd = -1;
EGLSyncKHR sync = eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
GL(glFlush());
if (sync == EGL_NO_SYNC_KHR) {
ALOGE("%s - Failed to Create Native Fence sync", __FUNCTION__);
} else {
fd = eglDupNativeFenceFDANDROID(eglGetCurrentDisplay(), sync);
if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
ALOGE("%s - Failed to dup sync", __FUNCTION__);
}
EGL(eglDestroySyncKHR(eglGetCurrentDisplay(), sync));
}
return fd;
}
//-----------------------------------------------------------------------------
void engine_setDestination(int id, int x, int y, int w, int h)
//-----------------------------------------------------------------------------
{
GL(glBindFramebuffer(GL_FRAMEBUFFER, id));
GL(glViewport(x, y, w, h));
}
//-----------------------------------------------------------------------------
void engine_setProgram(int id)
//-----------------------------------------------------------------------------
{
GL(glUseProgram(id));
}
//-----------------------------------------------------------------------------
void engine_set2DInputBuffer(int binding, unsigned int id)
//-----------------------------------------------------------------------------
{
GL(glActiveTexture(GL_TEXTURE0 + binding));
GL(glBindTexture(GL_TEXTURE_2D, id));
}
//-----------------------------------------------------------------------------
void engine_set3DInputBuffer(int binding, unsigned int id)
//-----------------------------------------------------------------------------
{
GL(glActiveTexture(GL_TEXTURE0 + binding));
GL(glBindTexture(GL_TEXTURE_3D, id));
}
//-----------------------------------------------------------------------------
void engine_setExternalInputBuffer(int binding, unsigned int id)
//-----------------------------------------------------------------------------
{
GL(glActiveTexture(GL_TEXTURE0 + binding));
GL(glBindTexture(0x8D65, id));
}
//-----------------------------------------------------------------------------
int engine_blit(int srcFenceFd)
//-----------------------------------------------------------------------------
{
int fd = -1;
WaitOnNativeFence(srcFenceFd);
float fullscreen_vertices[]{0.0f, 2.0f, 0.0f, 0.0f, 2.0f, 0.0f};
GL(glEnableVertexAttribArray(0));
GL(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, fullscreen_vertices));
GL(glDrawArrays(GL_TRIANGLES, 0, 3));
fd = CreateNativeFence();
GL(glFlush());
return fd;
}
//-----------------------------------------------------------------------------
void checkGlError(const char *file, int line)
//-----------------------------------------------------------------------------
{
for (GLint error = glGetError(); error; error = glGetError()) {
char *pError;
switch (error) {
case GL_NO_ERROR:
pError = (char *)"GL_NO_ERROR";
break;
case GL_INVALID_ENUM:
pError = (char *)"GL_INVALID_ENUM";
break;
case GL_INVALID_VALUE:
pError = (char *)"GL_INVALID_VALUE";
break;
case GL_INVALID_OPERATION:
pError = (char *)"GL_INVALID_OPERATION";
break;
case GL_OUT_OF_MEMORY:
pError = (char *)"GL_OUT_OF_MEMORY";
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
pError = (char *)"GL_INVALID_FRAMEBUFFER_OPERATION";
break;
default:
ALOGE("glError (0x%x) %s:%d\n", error, file, line);
return;
}
ALOGE("glError (%s) %s:%d\n", pError, file, line);
return;
}
return;
}
//-----------------------------------------------------------------------------
void checkEglError(const char *file, int line)
//-----------------------------------------------------------------------------
{
for (int i = 0; i < 5; i++) {
const EGLint error = eglGetError();
if (error == EGL_SUCCESS) {
break;
}
char *pError;
switch (error) {
case EGL_SUCCESS:
pError = (char *)"EGL_SUCCESS";
break;
case EGL_NOT_INITIALIZED:
pError = (char *)"EGL_NOT_INITIALIZED";
break;
case EGL_BAD_ACCESS:
pError = (char *)"EGL_BAD_ACCESS";
break;
case EGL_BAD_ALLOC:
pError = (char *)"EGL_BAD_ALLOC";
break;
case EGL_BAD_ATTRIBUTE:
pError = (char *)"EGL_BAD_ATTRIBUTE";
break;
case EGL_BAD_CONTEXT:
pError = (char *)"EGL_BAD_CONTEXT";
break;
case EGL_BAD_CONFIG:
pError = (char *)"EGL_BAD_CONFIG";
break;
case EGL_BAD_CURRENT_SURFACE:
pError = (char *)"EGL_BAD_CURRENT_SURFACE";
break;
case EGL_BAD_DISPLAY:
pError = (char *)"EGL_BAD_DISPLAY";
break;
case EGL_BAD_SURFACE:
pError = (char *)"EGL_BAD_SURFACE";
break;
case EGL_BAD_MATCH:
pError = (char *)"EGL_BAD_MATCH";
break;
case EGL_BAD_PARAMETER:
pError = (char *)"EGL_BAD_PARAMETER";
break;
case EGL_BAD_NATIVE_PIXMAP:
pError = (char *)"EGL_BAD_NATIVE_PIXMAP";
break;
case EGL_BAD_NATIVE_WINDOW:
pError = (char *)"EGL_BAD_NATIVE_WINDOW";
break;
case EGL_CONTEXT_LOST:
pError = (char *)"EGL_CONTEXT_LOST";
break;
default:
ALOGE("eglError (0x%x) %s:%d\n", error, file, line);
return;
}
ALOGE("eglError (%s) %s:%d\n", pError, file, line);
return;
}
return;
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __TONEMAPPER_GLENGINE_H__
#define __TONEMAPPER_GLENGINE_H__
#include <EGL/egl.h>
#define EGL_EGLEXT_PROTOTYPES
#include <EGL/eglext.h>
#include <GLES3/gl31.h>
#define GL_GLEXT_PROTOTYPES
#include <GLES2/gl2ext.h>
#include <GLES3/gl3ext.h>
#if defined(CHECK_GL_ERRORS)
#define GL(func) func;
#define EGL(func) func;
#else
#define GL(func) \
func; \
checkGlError(__FILE__, __LINE__);
#define EGL(func) \
func; \
checkEglError(__FILE__, __LINE__);
#endif
#define EGL_PROTECTED_CONTENT_EXT 0x32C0
void checkGlError(const char *file, int line);
void checkEglError(const char *file, int line);
#endif //__TONEMAPPER_GLENGINE_H__

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
* Not a Contribution.
*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const char* rgba_inverse_tonemap_shader = ""
"#extension GL_OES_EGL_image_external_essl3 : require \n"
"precision highp float; \n"
"precision highp sampler2D; \n"
"layout(binding = 0) uniform samplerExternalOES externalTexture; \n"
"layout(binding = 1) uniform sampler3D tonemapper; \n"
"layout(binding = 2) uniform sampler2D xform; \n"
"layout(location = 3) uniform vec2 tSO; \n"
"#if defined(USE_NONUNIFORM_SAMPLING) \n"
"layout(location = 4) uniform vec2 xSO; \n"
"#endif \n"
"in vec2 uv; \n"
"out vec4 fs_color; \n"
" \n"
"vec3 ScaleOffset(in vec3 samplePt, in vec2 so) \n"
"{ \n"
" vec3 adjPt = so.x * samplePt + so.y; \n"
" return adjPt; \n"
"} \n"
" \n"
"void main() \n"
"{ \n"
"vec2 flipped = vec2(uv.x, 1.0f - uv.y); \n"
"vec4 rgb_premulalpha = texture(externalTexture, flipped); \n"
"fs_color = rgb_premulalpha; \n"
"if( rgb_premulalpha.a > 0.0 ) { \n"
"vec3 rgb = rgb_premulalpha.rgb/rgb_premulalpha.a; \n"
"#if defined(USE_NONUNIFORM_SAMPLING) \n"
"vec3 adj = ScaleOffset(rgb.xyz, xSO); \n"
"float r = texture(xform, vec2(adj.r, 0.5f)).r; \n"
"float g = texture(xform, vec2(adj.g, 0.5f)).g; \n"
"float b = texture(xform, vec2(adj.b, 0.5f)).b; \n"
"#else \n"
"float r = rgb.r; \n"
"float g = rgb.g; \n"
"float b = rgb.b; \n"
"#endif \n"
"fs_color.rgb = texture(tonemapper, ScaleOffset(vec3(r, g, b), tSO)).rgb * rgb_premulalpha.a; \n"
"fs_color.a = rgb_premulalpha.a; \n"
"} \n"
"} \n";