package com.sun.prism.es2;
import com.sun.glass.ui.Screen;
import com.sun.prism.Graphics;
import com.sun.prism.Image;
import com.sun.prism.PixelFormat;
import com.sun.prism.RTTexture;
import com.sun.prism.ReadbackRenderTarget;
import com.sun.prism.Texture;
import com.sun.prism.impl.PrismSettings;
import com.sun.prism.impl.PrismTrace;
import java.nio.Buffer;
class ES2RTTexture extends ES2Texture<ES2RTTextureData>
implements ES2RenderTarget, RTTexture, ReadbackRenderTarget
{
private boolean opaque;
private ES2RTTexture(ES2Context context, ES2TextureResource<ES2RTTextureData> resource,
WrapMode wrapMode,
int physicalWidth, int physicalHeight,
int contentX, int contentY,
int contentWidth, int contentHeight,
int maxContentWidth, int maxContentHeight)
{
super(context, resource, PixelFormat.BYTE_BGRA_PRE, wrapMode,
physicalWidth, physicalHeight,
contentX, contentY,
contentWidth, contentHeight,
maxContentWidth, maxContentHeight, false);
PrismTrace.rttCreated(resource.getResource().getFboID(),
physicalWidth, physicalHeight,
PixelFormat.BYTE_BGRA_PRE.getBytesPerPixelUnit());
this.opaque = false;
}
void attachDepthBuffer(ES2Context context) {
ES2RTTextureData texData = resource.getResource();
int dbID = texData.getDepthBufferID();
if (dbID != 0) {
return;
}
int msaaSamples = isMSAA() ? context.getGLContext().getSampleSize() : 0;
dbID = context.getGLContext().createDepthBuffer(getPhysicalWidth(),
getPhysicalHeight(), msaaSamples);
texData.setDepthBufferID(dbID);
}
private void createAndAttachMSAABuffer(ES2Context context) {
ES2RTTextureData texData = resource.getResource();
int rbID = texData.getMSAARenderBufferID();
if (rbID != 0) {
return;
}
GLContext glContext = context.getGLContext();
rbID = glContext.createRenderBuffer(getPhysicalWidth(),
getPhysicalHeight(), glContext.getSampleSize());
texData.setMSAARenderBufferID(rbID);
}
static int getCompatibleDimension(ES2Context context, int dim, WrapMode wrapMode) {
GLContext glContext = context.getGLContext();
boolean pad;
switch (wrapMode) {
case CLAMP_NOT_NEEDED:
pad = false;
break;
case CLAMP_TO_ZERO:
pad = !glContext.canClampToZero();
break;
default:
case CLAMP_TO_EDGE:
case REPEAT:
throw new IllegalArgumentException("wrap mode not supported for RT textures: "+wrapMode);
case CLAMP_TO_EDGE_SIMULATED:
case CLAMP_TO_ZERO_SIMULATED:
case REPEAT_SIMULATED:
throw new IllegalArgumentException("Cannot request simulated wrap mode: "+wrapMode);
}
int paddedDim = pad ? dim + 2 : dim;
int maxSize = glContext.getMaxTextureSize();
int texDim;
if (glContext.canCreateNonPowTwoTextures()) {
texDim = (paddedDim <= maxSize) ? paddedDim : 0;
} else {
texDim = nextPowerOfTwo(paddedDim, maxSize);
}
if (texDim == 0) {
throw new RuntimeException(
"Requested texture dimension (" + dim + ") "
+ "requires dimension (" + texDim + ") "
+ "that exceeds maximum texture size (" + maxSize + ")");
}
texDim = Math.max(texDim, PrismSettings.minRTTSize);
return pad ? texDim - 2 : texDim;
}
static ES2RTTexture create(ES2Context context, int w, int h, WrapMode wrapMode, boolean msaa) {
GLContext glContext = context.getGLContext();
boolean pad;
switch (wrapMode) {
case CLAMP_NOT_NEEDED:
pad = false;
break;
case CLAMP_TO_ZERO:
pad = !glContext.canClampToZero();
break;
default:
case CLAMP_TO_EDGE:
case REPEAT:
throw new IllegalArgumentException("wrap mode not supported for RT textures: "+wrapMode);
case CLAMP_TO_EDGE_SIMULATED:
case CLAMP_TO_ZERO_SIMULATED:
case REPEAT_SIMULATED:
throw new IllegalArgumentException("Cannot request simulated wrap mode: "+wrapMode);
}
int contentX, contentY;
int paddedW, paddedH;
if (pad) {
contentX = 1;
contentY = 1;
paddedW = w + 2;
paddedH = h + 2;
wrapMode = wrapMode.simulatedVersion();
} else {
contentX = 0;
contentY = 0;
paddedW = w;
paddedH = h;
}
int maxSize = glContext.getMaxTextureSize();
int texWidth, texHeight;
if (glContext.canCreateNonPowTwoTextures()) {
texWidth = (paddedW <= maxSize) ? paddedW : 0;
texHeight = (paddedH <= maxSize) ? paddedH : 0;
} else {
texWidth = nextPowerOfTwo(paddedW, maxSize);
texHeight = nextPowerOfTwo(paddedH, maxSize);
}
if (texWidth == 0 || texHeight == 0) {
throw new RuntimeException(
"Requested texture dimensions (" + w + "x" + h + ") "
+ "require dimensions (" + texWidth + "x" + texHeight + ") "
+ "that exceed maximum texture size (" + maxSize + ")");
}
int minSize = PrismSettings.minRTTSize;
texWidth = Math.max(texWidth, minSize);
texHeight = Math.max(texHeight, minSize);
ES2VramPool pool = ES2VramPool.instance;
long size = pool.estimateRTTextureSize(texWidth, texHeight, false);
if (!pool.prepareForAllocation(size)) {
return null;
}
int contentW, contentH;
int maxContentW, maxContentH;
if (pad) {
maxContentW = texWidth - 2;
maxContentH = texHeight - 2;
contentW = w;
contentH = h;
} else {
maxContentW = texWidth;
maxContentH = texHeight;
contentW = w;
contentH = h;
}
glContext.setActiveTextureUnit(0);
int savedFBO = glContext.getBoundFBO();
int savedTex = glContext.getBoundTexture();
int nativeTexID = 0;
if (!msaa) {
nativeTexID = glContext.createTexture(texWidth, texHeight);
}
int nativeFBOID = 0;
if (nativeTexID != 0 || msaa) {
nativeFBOID = glContext.createFBO(nativeTexID);
if (nativeFBOID == 0) {
glContext.deleteTexture(nativeTexID);
nativeTexID = 0;
}
}
ES2RTTextureData texData =
new ES2RTTextureData(context, nativeTexID, nativeFBOID,
texWidth, texHeight, size);
ES2TextureResource<ES2RTTextureData> texRes = new ES2TextureResource<ES2RTTextureData>(texData);
ES2RTTexture es2RTT = new ES2RTTexture(context, texRes, wrapMode,
texWidth, texHeight,
contentX, contentY,
contentW, contentH,
maxContentW, maxContentH);
if (msaa) {
es2RTT.createAndAttachMSAABuffer(context);
}
glContext.bindFBO(savedFBO);
glContext.setBoundTexture(savedTex);
return es2RTT;
}
@Override
public Texture getBackBuffer() {
return this;
}
@Override
public Graphics createGraphics() {
return ES2Graphics.create(context, this);
}
@Override
public int[] getPixels() {
return null;
}
@Override
public boolean readPixels(Buffer pixels, int x, int y, int width, int height) {
context.flushVertexBuffer();
GLContext glContext = context.getGLContext();
int id = glContext.getBoundFBO();
int fboID = getFboID();
boolean changeBoundFBO = id != fboID;
if (changeBoundFBO) {
glContext.bindFBO(fboID);
}
boolean result = glContext.readPixels(pixels, x, y, width, height);
if (changeBoundFBO) {
glContext.bindFBO(id);
}
return result;
}
@Override
public boolean readPixels(Buffer pixels) {
return readPixels(pixels, getContentX(), getContentY(),
getContentWidth(), getContentHeight());
}
@Override
public int getFboID() {
return resource.getResource().getFboID();
}
@Override
public Screen getAssociatedScreen() {
return context.getAssociatedScreen();
}
@Override
public void update(Image img) {
throw new UnsupportedOperationException("update() not supported for RTTextures");
}
@Override
public void update(Image img, int dstx, int dsty) {
throw new UnsupportedOperationException("update() not supported for RTTextures");
}
@Override
public void update(Image img, int dstx, int dsty, int w, int h) {
throw new UnsupportedOperationException("update() not supported for RTTextures");
}
@Override
public void update(Image img, int dstx, int dsty, int w, int h, boolean skipFlush) {
throw new UnsupportedOperationException("update() not supported for RTTextures");
}
@Override
public void update(Buffer pixels, PixelFormat format,
int dstx, int dsty,
int srcx, int srcy, int srcw, int srch, int srcscan,
boolean skipFlush) {
throw new UnsupportedOperationException("update() not supported for RTTextures");
}
@Override
public boolean isOpaque() {
return opaque;
}
@Override
public void setOpaque(boolean opaque) {
this.opaque = opaque;
}
@Override
public boolean isVolatile() {
return false;
}
@Override
public boolean isMSAA() {
return resource.getResource().getMSAARenderBufferID() != 0;
}
}