Modifying RenderTexture data using Android Plugin

Hello!

I’m working on a cloud rendering project building the pipeline, and I’m looking into modifying Unity’s render buffer objects using an Android plugin. I managed to do this with D3D11 for PC, but I can’t do it for OpenGL ES. I’m looking for a solution to this.

Working on D3D11, I could work with a native render buffer pointer, via GetNativeRenderBufferPtr(). In OpenGL ES, I cannot work with the render buffer pointer. I can only work with a native texture using GetNativeTexturePtr(), which does not help me modify the RenderTexture object.

I can modify texture2D objects through the plugin, but I want to avoid this as much as possible since that requires CPU to GPU loading operations. TBH, I don’t know CPU to GPU loading is that bad for performance either.

Any help or guidance would be appreciated. Even simply getting a confirmation that it isn’t doable using a RenderTexture would be appreciated. Low-level rendering in Unity has been such a pain for me, so if you know of any resources please let me know!

Here’s my code (I had another go at it without FBOs and using glTexSubImage2D, which didn’t work either):


// Called from Unity to set the texture pointer.
void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API SetTexture(void* texturePtr, int width, int height) {
    gTexturePtr = texturePtr;
    gTexWidth = width;
    gTexHeight = height;
    image_data = generate_image(width, height, channels);
}

void* BeginModifyTexture(void* textureHandle, int textureWidth, int textureHeight, int* outRowPitch)
{
    const int rowPitch = textureWidth * 4;
    auto* data = new unsigned char[rowPitch * textureHeight];
    *outRowPitch = rowPitch;
    return data;
}

// Frame buffer solution
void EndModifyTexture(void* textureHandle, int textureWidth, int textureHeight, int rowPitch, void* dataPtr)
{
    auto gltex = (GLuint)(size_t)(textureHandle);
    
    if (!glIsTexture(gltex)) {
        LOGE("Invalid texture handle!");
        return;
    }
    
    if (fbo == 0) {
        glGenFramebuffers(1, &fbo);
    }
    
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gltex, 0);
    
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        LOGE("Framebuffer not complete!");
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        return;
    }
    
    GLuint tempTexture;
    glGenTextures(1, &tempTexture);
    glBindTexture(GL_TEXTURE_2D, tempTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, dataPtr);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
    GLuint tempFBO;
    glGenFramebuffers(1, &tempFBO);
    glBindFramebuffer(GL_FRAMEBUFFER, tempFBO);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tempTexture, 0);
    
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        LOGE("Temporary framebuffer not complete!");
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glDeleteFramebuffers(1, &tempFBO);
        glDeleteTextures(1, &tempTexture);
        return;
    }
    
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gltex, 0);
    
    glBindFramebuffer(GL_READ_FRAMEBUFFER, tempFBO);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
    glBlitFramebuffer(0, 0, textureWidth, textureHeight, 0, 0, textureWidth, textureHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
    
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
    glDeleteFramebuffers(1, &tempFBO);
    glDeleteTextures(1, &tempTexture);
    
    delete[] (unsigned char*)dataPtr;
}

void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API ProcessTextureEvent(int eventID) {
    
    if (eventID == 1 && gTexturePtr != nullptr) {
        
        int width = gTexWidth;
        int height = gTexHeight;
        
        int textureRowPitch;
        void* textureDataPtr = BeginModifyTexture(gTexturePtr, width, height, &textureRowPitch);
        
        auto* dst = (unsigned char*)textureDataPtr;
        
        for (int y = 0; y < height; ++y)
        {
            unsigned char* srcRow = image_data + (y * width * channels);
            unsigned char* dstRow = dst + (y * textureRowPitch);
            memcpy(dstRow, srcRow, width * channels);
        }
        
        EndModifyTexture(gTexturePtr, width, height, textureRowPitch, textureDataPtr);
    } else {
        LOGE("Invalid eventID or null texture pointer.");
    }
}