Add a new ptex mipmap loader. very slow...

This commit is contained in:
Takahito Tejima 2013-09-20 18:22:09 -07:00
parent c7d8368fff
commit d4579a8f85
5 changed files with 1130 additions and 1 deletions

View File

@ -114,16 +114,25 @@ add_definitions(
set(DOXY_HEADER_FILES ${PUBLIC_HEADER_FILES})
#-------------------------------------------------------------------------------
set(GL_PTEX_PUBLIC_HEADERS glPtexTexture.h)
set(GL_PTEX_PUBLIC_HEADERS
glPtexTexture.h
glPtexMipmapTexture.h
)
set(DX_PTEX_PUBLIC_HEADERS d3d11PtexTexture.h)
if( PTEX_FOUND )
list(APPEND PUBLIC_HEADER_FILES
ptexTextureLoader.h
ptexMipmapTextureLoader.h
)
list(APPEND CPU_SOURCE_FILES
ptexTextureLoader.cpp
ptexMipmapTextureLoader.cpp
)
if( OPENGL_FOUND )
list(APPEND GPU_SOURCE_FILES
glPtexTexture.cpp
glPtexMipmapTexture.cpp
)
list(APPEND PUBLIC_HEADER_FILES
${GL_PTEX_PUBLIC_HEADERS}

View File

@ -0,0 +1,139 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License
// and the following modification to it: Section 6 Trademarks.
// deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the
// trade names, trademarks, service marks, or product names of the
// Licensor and its affiliates, except as required for reproducing
// the content of the NOTICE file.
//
// 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 "../osd/glPtexMipmapTexture.h"
#include "../osd/ptexMipmapTextureLoader.h"
#include "../osd/opengl.h"
#include <Ptexture.h>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
OsdGLPtexMipmapTexture::OsdGLPtexMipmapTexture()
: _width(0), _height(0), _depth(0), _layout(0), _texels(0)
{
}
OsdGLPtexMipmapTexture::~OsdGLPtexMipmapTexture()
{
if (glIsTexture(_layout))
glDeleteTextures(1, &_layout);
if (glIsTexture(_texels))
glDeleteTextures(1, &_texels);
}
static GLuint
genTextureBuffer(GLenum format, GLsizeiptr size, GLvoid const * data)
{
GLuint buffer, result;
glGenBuffers(1, &buffer);
glBindBuffer(GL_TEXTURE_BUFFER, buffer);
glBufferData(GL_TEXTURE_BUFFER, size, data, GL_STATIC_DRAW);
glGenTextures(1, & result);
glBindTexture(GL_TEXTURE_BUFFER, result);
glTexBuffer(GL_TEXTURE_BUFFER, format, buffer);
// need to reset texture binding before deleting the source buffer.
glBindTexture(GL_TEXTURE_BUFFER, 0);
glDeleteBuffers(1, &buffer);
return result;
}
OsdGLPtexMipmapTexture *
OsdGLPtexMipmapTexture::Create(PtexTexture * reader, int maxLevels)
{
OsdGLPtexMipmapTexture * result = NULL;
GLint maxNumPages = 0;
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxNumPages);
// Read the ptexture data and pack the texels
OsdPtexMipmapTextureLoader loader(reader, maxNumPages, maxLevels);
// Setup GPU memory
int numFaces = loader.GetNumFaces();
GLuint layout = genTextureBuffer(GL_R16I,
numFaces * 6 * sizeof(GLshort),
loader.GetLayoutBuffer());
GLenum format, type;
switch (reader->dataType()) {
case Ptex::dt_uint16 : type = GL_UNSIGNED_SHORT; break;
case Ptex::dt_float : type = GL_FLOAT; break;
case Ptex::dt_half : type = GL_HALF_FLOAT; break;
default : type = GL_UNSIGNED_BYTE; break;
}
switch (reader->numChannels()) {
case 1 : format = GL_RED; break;
case 2 : format = GL_RG; break;
case 3 : format = GL_RGB; break;
case 4 : format = GL_RGBA; break;
default: format = GL_RED; break;
}
// actual texels texture array
GLuint texels;
glGenTextures(1, &texels);
glBindTexture(GL_TEXTURE_2D_ARRAY, texels);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0,
(type == GL_FLOAT) ? GL_RGBA32F : GL_RGBA,
loader.GetPageWidth(),
loader.GetPageHeight(),
loader.GetNumPages(),
0, format, type,
loader.GetTexelBuffer());
// loader.ClearBuffers();
// Return the Osd Ptexture object
result = new OsdGLPtexMipmapTexture;
result->_width = loader.GetPageWidth();
result->_height = loader.GetPageHeight();
result->_depth = loader.GetNumPages();
result->_format = format;
result->_layout = layout;
result->_texels = texels;
return result;
}
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,70 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License
// and the following modification to it: Section 6 Trademarks.
// deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the
// trade names, trademarks, service marks, or product names of the
// Licensor and its affiliates, except as required for reproducing
// the content of the NOTICE file.
//
// 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 OSD_GL_PTEX_MIPMAP_TEXTURE_H
#define OSD_GL_PTEX_MIPMAP_TEXTURE_H
#include "../version.h"
#include "../osd/nonCopyable.h"
#include "../osd/opengl.h"
class PtexTexture;
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
class OsdGLPtexMipmapTexture : OsdNonCopyable<OsdGLPtexMipmapTexture> {
public:
static OsdGLPtexMipmapTexture * Create(PtexTexture * reader,
int maxLevels=10);
/// Returns the texture buffer containing the layout of the ptex faces
/// in the texels texture array.
GLuint GetLayoutTextureBuffer() const { return _layout; }
/// Returns the texels texture array.
GLuint GetTexelsTexture() const { return _texels; }
~OsdGLPtexMipmapTexture();
private:
OsdGLPtexMipmapTexture();
GLsizei _width, // widht / height / depth of the 3D texel buffer
_height,
_depth;
GLint _format; // texel color format
GLuint _layout, // per-face lookup table
_texels; // texel data
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OSD_GL_PTEX_MIPMAP_TEXTURE_H

View File

@ -0,0 +1,775 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License
// and the following modification to it: Section 6 Trademarks.
// deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the
// trade names, trademarks, service marks, or product names of the
// Licensor and its affiliates, except as required for reproducing
// the content of the NOTICE file.
//
// 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 "../osd/ptexMipmapTextureLoader.h"
#include "../osd/error.h"
#include <Ptexture.h>
#include <list>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
// resample border texels for guttering
//
static int
resampleBorder(PtexTexture * ptex, int face, int edgeId, unsigned char *result,
int dstLength, int bpp, float srcStart=0.0f, float srcEnd=1.0f)
{
const Ptex::FaceInfo & pf = ptex->getFaceInfo(face);
int edgeLength = (edgeId==0||edgeId==2) ? pf.res.u() : pf.res.v();
int srcOffset = (int)(srcStart*edgeLength);
int srcLength = (int)((srcEnd-srcStart)*edgeLength);
// if dstLength < 0, returns as original resolution without scaling
if (dstLength < 0) dstLength = srcLength;
if (dstLength >= srcLength) {
PtexFaceData * data = ptex->getData(face);
unsigned char *border = new unsigned char[bpp*srcLength];
// order of the result will be flipped to match adjacent pixel order
for(int i=0;i<srcLength; ++i) {
int u = 0, v = 0;
if(edgeId==Ptex::e_bottom) {
u = edgeLength-1-(i+srcOffset);
v = 0;
} else if(edgeId==Ptex::e_right) {
u = pf.res.u()-1;
v = edgeLength-1-(i+srcOffset);
} else if(edgeId==Ptex::e_top) {
u = i+srcOffset;
v = pf.res.v()-1;
} else if(edgeId==Ptex::e_left) {
u = 0;
v = i+srcOffset;
}
data->getPixel(u, v, &border[i*bpp]);
}
// nearest resample to fit dstLength
for(int i=0;i<dstLength;++i) {
for(int j=0; j<bpp; j++) {
result[i*bpp+j] = border[(i*srcLength/dstLength)*bpp+j];
}
}
delete[] border;
} else {
Ptex::Res res = pf.res;
while (srcLength > dstLength && res.ulog2 && res.vlog2) {
--res.ulog2;
--res.vlog2;
srcLength /= 2;
}
PtexFaceData * data = ptex->getData(face, res);
unsigned char *border = new unsigned char[bpp*srcLength];
edgeLength = (edgeId==0||edgeId==2) ? res.u() : res.v();
srcOffset = (int)(srcStart*edgeLength);
for (int i = 0; i < dstLength; ++i) {
int u = 0, v = 0;
if (edgeId == Ptex::e_bottom) {
u = edgeLength-1-(i+srcOffset);
v = 0;
} else if(edgeId==Ptex::e_right) {
u = res.u()-1;
v = edgeLength-1-(i+srcOffset);
} else if(edgeId==Ptex::e_top) {
u = i+srcOffset;
v = res.v()-1;
} else if(edgeId==Ptex::e_left) {
u = 0;
v = i+srcOffset;
}
data->getPixel(u, v, &border[i*bpp]);
for (int j = 0; j < bpp; ++j) {
result[i*bpp+j] = border[i*bpp+j];
}
}
delete[] border;
}
return srcLength;
}
// flip order of pixel buffer
static void
flipBuffer(unsigned char *buffer, int length, int bpp)
{
for(int i=0; i<length/2; ++i){
for(int j=0; j<bpp; j++){
std::swap(buffer[i*bpp+j], buffer[(length-1-i)*bpp+j]);
}
}
}
// sample neighbor face's edge
static void
sampleNeighbor(PtexTexture * ptex, unsigned char *border, int face, int edge, int length, int bpp)
{
const Ptex::FaceInfo &fi = ptex->getFaceInfo(face);
// copy adjacent borders
int adjface = fi.adjface(edge);
if(adjface != -1) {
int ae = fi.adjedge(edge);
if (!fi.isSubface() && ptex->getFaceInfo(adjface).isSubface()) {
/* nonsubface -> subface (1:0.5) see http://ptex.us/adjdata.html for more detail
+------------------+
| face |
+--------edge------+
| adj face | |
+----------+-------+
*/
resampleBorder(ptex, adjface, ae, border, length/2, bpp);
const Ptex::FaceInfo &sfi1 = ptex->getFaceInfo(adjface);
adjface = sfi1.adjface((ae+3)%4);
ae = (sfi1.adjedge((ae+3)%4)+3)%4;
resampleBorder(ptex, adjface, ae, border+(length/2*bpp), length/2, bpp);
} else if (fi.isSubface() && !ptex->getFaceInfo(adjface).isSubface()) {
/* subface -> nonsubface (0.5:1). two possible configuration
case 1 case 2
+----------+----------+ +----------+----------+--------+
| face | B | | | face | B |
+---edge---+----------+ +----------+--edge----+--------+
|0.0 0.5 1.0| |0.0 0.5 1.0|
| adj face | | adj face |
+---------------------+ +---------------------+
*/
int Bf = fi.adjface((edge+1)%4);
int Be = fi.adjedge((edge+1)%4);
int f = ptex->getFaceInfo(Bf).adjface((Be+1)%4);
int e = ptex->getFaceInfo(Bf).adjedge((Be+1)%4);
if(f == adjface && e == ae) // case 1
resampleBorder(ptex, adjface, ae, border, length, bpp, 0.0, 0.5);
else // case 2
resampleBorder(ptex, adjface, ae, border, length, bpp, 0.5, 1.0);
} else {
/* ordinary case (1:1 match)
+------------------+
| face |
+--------edge------+
| adj face |
+----------+-------+
*/
resampleBorder(ptex, adjface, ae, border, length, bpp);
}
} else {
/* border edge. duplicate itself
+-----------------+
| face |
+-------edge------+
*/
resampleBorder(ptex, face, edge, border, length, bpp);
flipBuffer(border, length, bpp);
}
}
// get corner pixel by traversing all adjacent faces around vertex
//
static bool
getCornerPixel(PtexTexture *ptex, float *resultPixel, int numchannels,
int face, int edge, int bpp, int level, unsigned char *lineBuffer)
{
const Ptex::FaceInfo &fi = ptex->getFaceInfo(face);
/*
see http://ptex.us/adjdata.html Figure 2 for the reason of conditions edge==1 and 3
*/
if (fi.isSubface() && edge == 3) {
/*
in T-vertex case, this function sets 'D' pixel value to *resultPixel and returns false
gutter line
|
+------+-------+
| | |
| D|C |<-- gutter line
| *-------+
| B|A [2] |
| |[3] [1]|
| | [0] |
+------+-------+
*/
int adjface = fi.adjface(edge);
if (adjface != -1 and !ptex->getFaceInfo(adjface).isSubface()) {
int length = resampleBorder(ptex,
adjface,
fi.adjedge(edge),
lineBuffer,
/*dstLength=*/-1,
bpp,
0.0f, 1.0f);
/* then lineBuffer contains
|-------DB-------|
0 ^ length-1
length/2-1
*/
Ptex::ConvertToFloat(resultPixel,
lineBuffer + bpp*(length/2-1),
ptex->dataType(),
numchannels);
return true;
}
}
if (fi.isSubface() && edge == 1) {
/* gutter line
|
+------+-------+
| | [3] |
| |[0] [2]|
| B|A [1] |
| *-------+
| D|C |<-- gutter line
| | |
+------+-------+
note: here we're focusing on vertex A which corresponds to the edge 1,
but the edge 0 is an adjacent edge to get D pixel.
*/
int adjface = fi.adjface(0);
if (adjface != -1 and !ptex->getFaceInfo(adjface).isSubface()) {
int length = resampleBorder(ptex,
adjface,
fi.adjedge(0),
lineBuffer,
/*dstLength=*/-1,
bpp,
0.0f, 1.0f);
/* then lineBuffer contains
|-------BD-------|
0 ^ length-1
length/2
*/
Ptex::ConvertToFloat(resultPixel,
lineBuffer + bpp*(length/2),
ptex->dataType(),
numchannels);
return true;
}
}
int currentFace = face;
int currentEdge = edge;
int uv[4][2] = {{0,0}, {1,0}, {1,1}, {0,1}};
float *pixel = (float*)alloca(sizeof(float)*numchannels);
float *accumPixel = (float*)alloca(sizeof(float)*numchannels);
// clear accum pixel
memset(accumPixel, 0, sizeof(float)*numchannels);
bool clockWise = true;
int nFace = 0;
do {
nFace++;
if (nFace > 255) {
OsdWarning("High valence detected in %s : invalid adjacency around "
"face %d", ptex->path(), face);
break;
}::
Ptex::FaceInfo info = ptex->getFaceInfo(currentFace);
int ulog2 = std::max(0, info.res.ulog2 - level);
int vlog2 = std::max(0, info.res.vlog2 - level);
Ptex::Res res(ulog2, vlog2);
ptex->getPixel(currentFace,
uv[currentEdge][0] * (res.u()-1),
uv[currentEdge][1] * (res.v()-1),
pixel, 0, numchannels, res);
for (int j = 0; j < numchannels; ++j) {
accumPixel[j] += pixel[j];
if (nFace == 3) {
resultPixel[j] = pixel[j];
}
}
// next face
if (clockWise) {
currentFace = info.adjface(currentEdge);
currentEdge = info.adjedge(currentEdge);
currentEdge = (currentEdge+1)%4;
} else {
currentFace = info.adjface((currentEdge+3)%4);
currentEdge = info.adjedge((currentEdge+3)%4);
}
if (currentFace == -1) {
// border case.
if (clockWise) {
// reset position and restart counter clock wise
Ptex::FaceInfo sinfo = ptex->getFaceInfo(face);
currentFace = sinfo.adjface((edge+3)%4);
currentEdge = sinfo.adjedge((edge+3)%4);
clockWise = false;
} else {
// end
break;
}
}
} while(currentFace != -1 and currentFace != face);
if (nFace == 4) {
return true;
}
// non-4 valence. let's average and return false;
for (int j = 0; j < numchannels; ++j) {
resultPixel[j] = accumPixel[j]/nFace;
}
return false;
}
// sample neighbor pixels and populate around blocks
void
OsdPtexMipmapTextureLoader::Block::guttering(PtexTexture *ptex, int level, int width, int height,
unsigned char *pptr, int bpp, int stride)
{
// XXX: fixme
unsigned char * lineBuffer = new unsigned char[16384 * bpp];
for(int edge=0; edge<4; edge++) {
int len = (edge==0 or edge==2) ? width : height;
sampleNeighbor(ptex, lineBuffer, this->index, edge, len, bpp);
unsigned char *s = lineBuffer, *d;
for(int j=0;j<len;++j) {
d = pptr;
switch(edge) {
case Ptex::e_bottom:
d += bpp * (j + 1); //stride*b->v + bpp*(b->u + j);
break;
case Ptex::e_right:
d += stride * (j + 1) + bpp * (width+1); // stride*(b->v + j) + bpp*(b->u + res.u() +1);
break;
case Ptex::e_top:
d += stride * (height+1) + bpp*(len-j); //stride*(b->v + res.v()+1) + bpp*(b->u + len-j-1);
break;
case Ptex::e_left:
d += stride * (len-j); //stride*(b->v + len-j-1) + bpp*(b->u);
break;
}
for(int k=0; k<bpp; k++)
*d++ = *s++;
}
}
// fix corner pixels
int numchannels = ptex->numChannels();
float *accumPixel = new float[numchannels];
int uv[4][2] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
for (int edge=0; edge<4; edge++) {
int du = uv[edge][0];
int dv = uv[edge][1];
/* There are 3 cases when filling a corner pixel on gutter.
case 1: Regular 4 valence
We already have correct 'B' and 'C' pixels by edge resampling above.
so here only one more pixel 'D' is needed,
and it will be placed on the gutter corner.
+-----+-----+
| | |<-current
| B|A |
+-----*-----+
| D|C |
| | |
+-----+-----+
case 2: T-vertex case (note that this doesn't mean 3 valence)
If the current face comes from non-quad root face, there could be a T-vertex
on its corner. Just like case 1, need to fill border corner with pixel 'D'.
+-----+-----+
| | |<-current
| B|A |
| *-----+
| D|C |
| | |
+-----+-----+
case 3: Other than 4 valence case (everything else, including boundary)
Since guttering pixels are placed on the border of each ptex faces,
It's not possible to store more than 4 pixels at a coner for a reasonable
interpolation.
In this case, we need to average all corner pixels and overwrite with an
averaged value, so that every face vertex picks the same value.
+---+---+
| | |<-current
| B|A |
+---*---|
| D/E\C |
| / \ |
|/ \|
+-------+
*/
if (getCornerPixel(ptex, accumPixel, numchannels, this->index, edge, bpp, level, lineBuffer)) {
// case 1 and case 2
if (edge==1||edge==2) du += width;
if (edge==2||edge==3) dv += height;
unsigned char *d = pptr + dv*stride + du*bpp;
Ptex::ConvertFromFloat(d, accumPixel, ptex->dataType(), numchannels);
} else {
// case 3, set accumPixel to the corner 4 pixels
if (edge==1||edge==2) du += width - 1;
if (edge==2||edge==3) dv += height - 1;
for (int u = 0; u < 2; ++u) {
for (int v = 0; v < 2; ++v) {
unsigned char *d = pptr + (dv+u)*stride + (du+v)*bpp;
Ptex::ConvertFromFloat(d, accumPixel, ptex->dataType(), numchannels);
}
}
}
}
delete[] lineBuffer;
delete[] accumPixel;
}
void
OsdPtexMipmapTextureLoader::Block::Generate(PtexTexture *ptex, unsigned char *destination,
int bpp, int width, int maxLevels)
{
const Ptex::FaceInfo &faceInfo = ptex->getFaceInfo(index);
int stride = bpp * width;
Ptex::Res res = faceInfo.res;
int ulog2 = res.ulog2;
int vlog2 = res.vlog2;
int level = 0;
int uofs = u, vofs = v;
while(ulog2 >= 2 and vlog2 >= 2 and level <= maxLevels) {
if (level % 2 == 1)
uofs += (1<<(ulog2+1))+2;
if ((level > 0) and (level % 2 == 0))
vofs += (1<<(vlog2+1)) + 2;
unsigned char *dst = destination + vofs * stride + uofs * bpp;
unsigned char *dstData = destination + (vofs + 1) * stride + (uofs + 1) * bpp;
ptex->getData(index, dstData, stride, Ptex::Res(ulog2, vlog2));
guttering(ptex, level, 1<<ulog2, 1<<vlog2, dst, bpp, stride);
--ulog2;
--vlog2;
++level;
}
nMipmaps = level;
}
/*
page :
*/
struct OsdPtexMipmapTextureLoader::Page {
struct Slot {
Slot(unsigned short u, unsigned short v,
unsigned short w, unsigned short h) :
u(u), v(v), width(w), height(h) { }
unsigned short u, v, width, height;
// returns true if a block can fit in this slot
bool Fits(const Block *block) {
return (block->width <= width) and (block->height <= height);
}
};
typedef std::list<Block *> BlockList;
Page(unsigned short width, unsigned short height) {
_slots.push_back(Slot(0, 0, width, height));
}
bool IsFull() const {
return _slots.empty();
}
// true when the block "b" is successfully added to this page :
//
// |--------------------------| |------------|-------------|
// | | |............| |
// | | |............| |
// | | |.... B .....| Right Slot |
// | | |............| |
// | | |............| |
// | | |------------|-------------|
// | Original Slot | ==> | |
// | | | |
// | | | Bottom Slot |
// | | | |
// | | | |
// |--------------------------| |--------------------------|
//
bool AddBlock(Block *block) {
for (SlotList::iterator it = _slots.begin(); it != _slots.end(); ++it) {
if (it->Fits(block)) {
_blocks.push_back(block);
block->u = it->u;
block->v = it->v;
// add new slot to the right
if (it->width > block->width) {
_slots.push_front(Slot(it->u + block->width,
it->v,
it->width - block->width,
block->height));
}
// add new slot to the bottom
if (it->height > block->height) {
_slots.push_back(Slot(it->u,
it->v + block->height,
it->width,
it->height - block->height));
}
_slots.erase(it);
return true;
}
}
return false;
}
void Generate(PtexTexture *ptex, unsigned char *destination,
int bpp, int width, int maxLevels) {
for (BlockList::iterator it = _blocks.begin(); it != _blocks.end(); ++it) {
(*it)->Generate(ptex, destination, bpp, width, maxLevels);
}
}
const BlockList &GetBlocks() const {
return _blocks;
}
void Dump() const {
for (BlockList::const_iterator it = _blocks.begin(); it != _blocks.end(); ++it) {
printf(" (%d, %d) %d x %d\n",
(*it)->u, (*it)->v, (*it)->width, (*it)->height);
}
}
private:
BlockList _blocks;
typedef std::list<Slot> SlotList;
SlotList _slots;
};
OsdPtexMipmapTextureLoader::OsdPtexMipmapTextureLoader(PtexTexture *ptex,
int maxNumPages,
int maxLevels) :
_ptex(ptex), _maxLevels(maxLevels), _bpp(0),
_pageWidth(0), _pageHeight(0), _texelBuffer(NULL)
{
// byte per pixel
_bpp = ptex->numChannels() * Ptex::DataSize(ptex->dataType());
int numFaces = ptex->numFaces();
_blocks.resize(numFaces);
for (int i = 0; i < numFaces; ++i) {
const Ptex::FaceInfo &faceInfo = ptex->getFaceInfo(i);
_blocks[i].index = i;
int w = faceInfo.res.u();
int h = faceInfo.res.v();
_blocks[i].texWidth = w;
_blocks[i].texHeight = h;
// XXX: each face must have at least 2x2 texels
w = w + w/2 + 4;
h = h + 2;
_blocks[i].width = w;
_blocks[i].height = h;
}
optimizePacking(maxNumPages);
generateBuffers();
}
OsdPtexMipmapTextureLoader::~OsdPtexMipmapTextureLoader()
{
delete _texelBuffer;
}
void
OsdPtexMipmapTextureLoader::optimizePacking(int maxNumPages)
{
int numTexels = 0;
// prepare a vector of pointers
std::vector<Block *> blocks(_blocks.size());
for (size_t i = 0; i < _blocks.size(); ++i) {
Block *block = &_blocks[i];
blocks[i] = block;
numTexels += block->width * block->height;
}
// sort blocks by height-width order
std::sort(blocks.begin(), blocks.end(), Block::sort);
// compute page size ---------------------------------------------
// page size is set to the largest edge of the largest block : this is the
// smallest possible page size, which should minimize the texels wasted on
// the "last page" when the smallest blocks are being packed.
// minumum
_pageWidth = 512 + 256 + 4;
_pageHeight = 512 + 2;
for (size_t i = 0; i < _blocks.size(); ++i) {
Block *block = &_blocks[i];
_pageWidth = std::max(_pageWidth, (int)block->width);
_pageHeight = std::max(_pageHeight, (int)block->height);
}
//int maxNumPages = 2048;
// grow the pagesize to make sure the optimization will not exceed the maximum
// number of pages allowed
// for (int npages=_txc/(_pagesize*_pagesize); npages>maxnumpages; _pagesize<<=1)
// npages = _txc/(_pagesize*_pagesize );
// pack blocks into slots ----------------------------------------
for (size_t i = 0, firstslot = 0; i < _blocks.size(); ++i) {
Block *block = blocks[i];
// traverse existing pages for a suitable slot ---------------
bool added = false;
for (size_t p = firstslot; p < _pages.size(); ++p) {
if ( (added = _pages[p]->AddBlock(block)) == true) {
break;
}
}
// if none of page was found : start new page
if (!added) {
Page *page = new Page(_pageWidth, _pageHeight);
added = page->AddBlock(block);
assert(added);
_pages.push_back(page);
}
// adjust the page flag to the first page with open slots
if (_pages.size() > (firstslot+1) and
_pages[firstslot+1]->IsFull()) ++firstslot;
}
printf("PageSize = %d x %d x %d\n", _pageWidth, _pageHeight, (int)_pages.size());
#if 0
for (size_t i = 0; i < _pages.size(); ++i) {
printf("Page %ld : \n", i);
_pages[i]->Dump();
}
int allSize = _pageWidth * _pageHeight * (int)_pages.size();
printf("Utilization %d/%d %.2f%%\n",
numTexels, allSize, 100*numTexels/float(allSize));
#endif
}
void
OsdPtexMipmapTextureLoader::generateBuffers()
{
// ptex layout struct
// struct Layout {
// unsigned short page;
// unsigned short nMipmap;
// unsigned short u;
// unsigned short v;
// unsigned short width;
// unsigned short height;
// };
int numFaces = (int)_blocks.size();
int numPages = (int)_pages.size();
// populate the texels
int pageStride = _bpp * _pageWidth * _pageHeight;
_texelBuffer = new unsigned char[pageStride * numPages];
memset(_texelBuffer, 0, pageStride * numPages);
for (int i = 0; i < numPages; ++i) {
printf("%d/%d\r", i, numPages);
_pages[i]->Generate(_ptex, _texelBuffer + pageStride * i,
_bpp, _pageWidth, _maxLevels);
}
// populate the layout texture buffer
_layoutBuffer = new unsigned char[numFaces * sizeof(short) * 6];
for (int i = 0; i < numPages; ++i) {
Page *page = _pages[i];
for (Page::BlockList::const_iterator it = page->GetBlocks().begin();
it != page->GetBlocks().end(); ++it) {
int ptexIndex = (*it)->index;
unsigned short *p = (unsigned short*)(_layoutBuffer + sizeof(short)*6*ptexIndex);
*p++ = i; // page
*p++ = (*it)->nMipmaps-1;
*p++ = (*it)->u+1;
*p++ = (*it)->v+1;
*p++ = (*it)->texWidth;
*p++ = (*it)->texHeight;
}
}
#if 0
// debug
FILE *fp = fopen("out.ppm", "w");
fprintf(fp, "P3\n");
fprintf(fp, "%d %d\n", _pageWidth, _pageHeight * numPages);
fprintf(fp, "255\n");
unsigned char *p = _texelBuffer;
for (int i = 0; i < numPages; ++i) {
for (int y = 0; y < _pageHeight; ++y) {
for (int x = 0; x < _pageWidth; ++x) {
fprintf(fp, "%d %d %d ", (int)p[0], (int)p[1], (int)p[2]);
p += 3;
}
fprintf(fp, "\n");
}
}
fclose(fp);
#endif
}
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@ -0,0 +1,136 @@
//
// Copyright 2013 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License
// and the following modification to it: Section 6 Trademarks.
// deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the
// trade names, trademarks, service marks, or product names of the
// Licensor and its affiliates, except as required for reproducing
// the content of the NOTICE file.
//
// 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 OSD_PTEX_MIPMAP_TEXTURE_LOADER_H
#define OSD_PTEX_MIPMAP_TEXTURE_LOADER_H
#include "../version.h"
#include <vector>
class PtexTexture;
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
class OsdPtexMipmapTextureLoader {
public:
OsdPtexMipmapTextureLoader(PtexTexture *ptex,
int maxNumPages, int maxLevels=10);
~OsdPtexMipmapTextureLoader();
const unsigned char * GetLayoutBuffer() const {
return _layoutBuffer;
}
const unsigned char * GetTexelBuffer() const {
return _texelBuffer;
}
int GetNumFaces() const {
return (int)_blocks.size();
}
int GetNumPages() const {
return (int)_pages.size();
}
int GetPageWidth() const {
return _pageWidth;
}
int GetPageHeight() const {
return _pageHeight;
}
/*
block : atomic texture unit
XXX: face of 128x128 or more (64kb~) texels should be considered separately
using ARB_sparse_texture...?
. : per-face texels for each mipmap level
x : guttering pixel
xxxxxxxxxxxxxx
x........xx..x 2x2
x........xx..x
x........xxxxx
x..8x8...xxxxxxx
x........xx....x
x........xx....x 4x4
x........xx....x
x........xx....x
xxxxxxxxxxxxxxxx
For each face (w*h), texels with guttering and mipmap is stored into
(w+2+w/2+2)*(h+2) area as above.
*/
/*
Ptex loader
Texels buffer : the packed texels
*/
private:
struct Block {
int index; // ptex index
int nMipmaps;
unsigned short u, v; // top-left texel offset
unsigned short width, height; // texel dimension (includes mipmap)
unsigned short texWidth, texHeight; // texel dimension (original tile)
void Generate(PtexTexture *ptex, unsigned char *destination,
int bpp, int width, int maxLevels);
void guttering(PtexTexture *ptex, int level, int width, int height,
unsigned char *pptr, int bpp, int stride);
static bool sort(const Block *a, const Block *b) {
return (a->height > b->height) or
((a->height == b->height) and (a->width > b->width));
}
};
struct Page;
void generateBuffers();
void optimizePacking(int maxNumPages);
std::vector<Block> _blocks;
std::vector<Page *> _pages;
PtexTexture *_ptex;
int _maxLevels;
int _bpp;
int _pageWidth, _pageHeight;
unsigned char *_texelBuffer;
unsigned char *_layoutBuffer;
};
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OSD_PTEX_MIPMAP_TEXTURE_LOADER_H