void FTGLTextureFont::CalculateTextureSize() { if (!maximumGLTextureSize) { glGetIntegerv (GL_MAX_TEXTURE_SIZE, (GLint*)&maximumGLTextureSize); } textureWidth = NextPowerOf2 ((remGlyphs * glyphWidth) + (padding * 2)); textureWidth = textureWidth > maximumGLTextureSize ? maximumGLTextureSize : textureWidth; int h = static_cast<int> ((textureWidth - (padding * 2)) / glyphWidth); textureHeight = NextPowerOf2 (( (numGlyphs / h) + 1) * glyphHeight); textureHeight = textureHeight > maximumGLTextureSize ? maximumGLTextureSize : textureHeight; }
////////////////////////////////////////////////////////////////////////////// // Initialize() // ////////////////////////////////////////////////////////////////////////////// void Initialize() { CalculateSurfaceSize(); m_bTextureSize = m_sizeSurface.Y() == (int)NextPowerOf2((DWORD)m_sizeSurface.Y()) && m_sizeSurface.X() == (int)NextPowerOf2((DWORD)m_sizeSurface.X()); m_bColorKey = false; m_colorKey = Color(0, 0, 0); m_data.m_id = 0; m_mode = SurfaceModeDD; m_pbits = NULL; }
void FTGLTextureFont::GetSize() { //work out the max width. Most likely maxTextSize textureWidth = NextPowerOf2( (remGlyphs * glyphWidth) + padding * 2); if( textureWidth > maxTextSize) { textureWidth = maxTextSize; } int h = static_cast<int>( (textureWidth - padding * 2) / glyphWidth); textureHeight = NextPowerOf2( (( numGlyphs / h) + 1) * glyphHeight); textureHeight = textureHeight > maxTextSize ? maxTextSize : textureHeight; }
void Backbuffer::CreateTexture(uint32_t width, uint32_t height) { m_TextureSize = NextPowerOf2(std::max(width, height)); glGenTextures(1, &m_Texture); glBindTexture(GL_TEXTURE_2D, m_Texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureSize, m_TextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); }
void *decodeAVAudioStream(StreamPtr stream, size_t *length) { char *outbuf = NULL; size_t buflen = 0; void *inbuf; size_t got; *length = 0; if(!stream || stream->CodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) return NULL; while((inbuf=getAVAudioData(stream, &got)) != NULL && got > 0) { void *ptr; ptr = realloc(outbuf, NextPowerOf2(buflen+got)); if(ptr == NULL) break; outbuf = (char*)ptr; memcpy(&outbuf[buflen], inbuf, got); buflen += got; } outbuf = (char*)realloc(outbuf, buflen); *length = buflen; return outbuf; }
void FTGLTextureFont::CalculateTextureSize() { if( !maximumGLTextureSize) { glGetIntegerv( GL_MAX_TEXTURE_SIZE, (GLint*)&maximumGLTextureSize); assert(maximumGLTextureSize); // If you hit this then you have an invalid OpenGL context. } textureWidth = NextPowerOf2( (remGlyphs * glyphWidth) + ( padding * 2)); textureWidth = textureWidth > maximumGLTextureSize ? maximumGLTextureSize : textureWidth; int h = static_cast<int>( (textureWidth - ( padding * 2)) / glyphWidth); textureHeight = NextPowerOf2( (( numGlyphs / h) + 1) * glyphHeight); textureHeight = textureHeight > maximumGLTextureSize ? maximumGLTextureSize : textureHeight; }
bool FFTConvolver::init(size_t blockSize, const Sample* ir, size_t irLen) { reset(); if (blockSize == 0) { return false; } // Ignore zeros at the end of the impulse response because they only waste computation time while (irLen > 0 && ::fabs(ir[irLen-1]) < 0.000001f) { --irLen; } if (irLen == 0) { return true; } _blockSize = NextPowerOf2(blockSize); _segSize = 2 * _blockSize; _segCount = static_cast<size_t>(::ceil(static_cast<float>(irLen) / static_cast<float>(_blockSize))); _fftComplexSize = audiofft::AudioFFT::ComplexSize(_segSize); // FFT _fft.init(_segSize); _fftBuffer.resize(_segSize); // Prepare segments for (size_t i=0; i<_segCount; ++i) { _segments.push_back(new SplitComplex(_fftComplexSize)); } // Prepare IR for (size_t i=0; i<_segCount; ++i) { SplitComplex* segment = new SplitComplex(_fftComplexSize); const size_t remaining = irLen - (i * _blockSize); const size_t sizeCopy = (remaining >= _blockSize) ? _blockSize : remaining; CopyAndPad(_fftBuffer, &ir[i*_blockSize], sizeCopy); _fft.fft(_fftBuffer.data(), segment->re(), segment->im()); _segmentsIR.push_back(segment); } // Prepare convolution buffers _preMultiplied.resize(_fftComplexSize); _conv.resize(_fftComplexSize); _overlap.resize(_blockSize); // Prepare input buffer _inputBuffer.resize(_blockSize); _inputBufferFill = 0; // Reset current position _current = 0; return true; }
static ALboolean ALchorusState_deviceUpdate(ALchorusState *state, ALCdevice *Device) { ALuint maxlen; ALuint it; maxlen = fastf2u(AL_CHORUS_MAX_DELAY * 3.0f * Device->Frequency) + 1; maxlen = NextPowerOf2(maxlen); if(maxlen != state->BufferLength) { void *temp; temp = realloc(state->SampleBuffer[0], maxlen * sizeof(ALfloat) * 2); if(!temp) return AL_FALSE; state->SampleBuffer[0] = temp; state->SampleBuffer[1] = state->SampleBuffer[0] + maxlen; state->BufferLength = maxlen; } for(it = 0;it < state->BufferLength;it++) { state->SampleBuffer[0][it] = 0.0f; state->SampleBuffer[1][it] = 0.0f; } return AL_TRUE; }
static ALboolean EchoDeviceUpdate(ALeffectState *effect, ALCdevice *Device) { ALechoState *state = (ALechoState*)effect; ALuint maxlen, i; // Use the next power of 2 for the buffer length, so the tap offsets can be // wrapped using a mask instead of a modulo maxlen = (ALuint)(AL_ECHO_MAX_DELAY * Device->Frequency) + 1; maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1; maxlen = NextPowerOf2(maxlen); if(maxlen != state->BufferLength) { void *temp; temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfp)); if(!temp) return AL_FALSE; state->SampleBuffer = temp; state->BufferLength = maxlen; } for(i = 0; i < state->BufferLength; i++) state->SampleBuffer[i] = int2ALfp(0); for(i = 0; i < MAXCHANNELS; i++) state->Gain[i] = int2ALfp(0); for(i = 0; i < Device->NumChan; i++) { Channel chan = Device->Speaker2Chan[i]; state->Gain[chan] = int2ALfp(1); } return AL_TRUE; }
static ALboolean EchoDeviceUpdate(ALeffectState *effect, ALCdevice *Device) { ALechoState *state = (ALechoState*)effect; ALuint maxlen, i; // Use the next power of 2 for the buffer length, so the tap offsets can be // wrapped using a mask instead of a modulo maxlen = (ALuint)(AL_ECHO_MAX_DELAY * Device->Frequency) + 1; maxlen += (ALuint)(AL_ECHO_MAX_LRDELAY * Device->Frequency) + 1; maxlen = NextPowerOf2(maxlen); if(maxlen != state->BufferLength) { void *temp; temp = realloc(state->SampleBuffer, maxlen * sizeof(ALfloat)); if(!temp) return AL_FALSE; state->SampleBuffer = temp; state->BufferLength = maxlen; } for(i = 0;i < state->BufferLength;i++) state->SampleBuffer[i] = 0.0f; return AL_TRUE; }
Texture::Texture(Bitmap& bmp) : m_ImageHeight(bmp.GetHeight()), m_ImageWidth(bmp.GetWidth()), m_TextureWidth(NextPowerOf2(m_ImageWidth)), m_TextureHeight(NextPowerOf2(m_ImageHeight)), m_TextureID(0) { bmp.FlipAroundXAxis(); glGenTextures(1, &m_TextureID); glBindTexture(GL_TEXTURE_2D, m_TextureID); //glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // GL_LINEAR_MIPMAP_NEAREST glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureWidth, m_TextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_ImageWidth, m_ImageHeight, GL_RGBA, GL_UNSIGNED_BYTE, bmp.GetData()); }
// Calculate the length of a delay line and store its mask and offset. static ALuint CalcLineLength(ALfloat length, ALintptrEXT offset, ALuint frequency, DelayLine *Delay) { ALuint samples; // All line lengths are powers of 2, calculated from their lengths, with // an additional sample in case of rounding errors. samples = NextPowerOf2((ALuint)(length * frequency) + 1); // All lines share a single sample buffer. Delay->Mask = samples - 1; Delay->Line = (ALfloat*)offset; // Return the sample count for accumulation. return samples; }
ms_uint32 OglContext::getTextureSize(GLuint dimension, ms_uint32 value) { glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, value, value, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); GLint check = 0; glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, dimension, &check); if (glGetError() != GL_NO_ERROR) { msSetError(MS_OGLERR, "glGetTexLevelParameteriv failed. glError: %d", "OglContext::getTextureSize()", glGetError()); } if (check == 0) { return MS_MAX(MS_MIN(NextPowerOf2(value), OglContext::MAX_TEXTURE_SIZE), OglContext::MIN_TEXTURE_SIZE); } else { msSetError(MS_OGLERR, "Unable to create opengl texture of map size", "OglContext::getTextureSize()"); return value; } }
static char *expdup(const char *str) { char *output = NULL; size_t maxlen = 0; size_t len = 0; while(*str != '\0') { const char *addstr; size_t addstrlen; size_t i; if(str[0] != '$') { const char *next = strchr(str, '$'); addstr = str; addstrlen = next ? (size_t)(next-str) : strlen(str); str += addstrlen; } else { str++; if(*str == '$') { const char *next = strchr(str+1, '$'); addstr = str; addstrlen = next ? (size_t)(next-str) : strlen(str); str += addstrlen; } else { char envname[1024]; size_t k = 0; while((isalnum(*str) || *str == '_') && k < sizeof(envname)-1) envname[k++] = *(str++); envname[k++] = '\0'; if((addstr=getenv(envname)) == NULL) continue; addstrlen = strlen(addstr); } } if(addstrlen == 0) continue; if(addstrlen >= maxlen-len) { void *temp = NULL; size_t newmax; newmax = NextPowerOf2(len+addstrlen+1); if(newmax > maxlen) temp = realloc(output, newmax); if(!temp) { ERR("Failed to realloc %lu bytes from %lu!\n", newmax, maxlen); return output; } output = temp; maxlen = newmax; } for(i = 0;i < addstrlen;i++) output[len++] = addstr[i]; output[len] = '\0'; } return output ? output : calloc(1, 1); }
void * GCAllocator::Allocate(int size, bool afterGC) { AllocStructure * ptr; // Old obsolete comments: // Either the small object allocator on the corresponding is empty // or we are allocating a bigger size // For this implementation, we are not differentiating between medium and bigger size... // I.e. 2Kb / 16 Kb / 100 Kb will be allocated on the same buffer // We might certainly want to improve this later... // Here it means that we reached the end of the buffer // First look at the free block that are sparkled in the buffer // Start by the closest size and continue until the biggest // We use the next power of 2 to make sure the next block is big enough // In reality we should try to have a best fit (that one is in between first fit / best fit) // Or at least a better fit ;) if (size <= SMALL_SIZE_BIN) { // Allocation that happens most of the time // First look at the small object allocator // We need to assume that the small object allocator is filled enough // By doing this we reuse memory that has been allocated and freed before // This reduces memory consumption and fragmentation as well /* int indexSmallBin = size >> ALIGNMENT_SHIFT; ptr = sSmallBin[indexSmallBin]; if (ptr != NULL) { sSmallBin[indexSmallBin] = ptr->mNext; // Done! // Cost: 2 tests, 1 operation, 2 reads, 1 write return (ptr); } */ int alignedSize = Align(size); // Special optimization for small size ptr = sCurrentMediumPointer; if (ptr != NULL) { CROSSNET_ASSERT(IsAligned(ptr), ""); CROSSNET_ASSERT(IsAligned(sCurrentMediumSize), ""); // Good news, there is already a cached medium size // if (sCurrentMediumSize >= size) // For whatever reason, this is actually crashing... Use the bigger size, // this should not change tremendously the performance... // TODO: Investigate this... if (sCurrentMediumSize >= SMALL_SIZE_BIN) { CROSSNET_ASSERT(sCurrentMediumSize >= alignedSize, ""); // And the buffer is big enough... // ptr is going to be the allocated pointer // Note that when we use the medium buffer cache, we are not reading / writing anything more // I.e. we are not pushing free blocks all over the place, nor poping / pushing in the medium size bin // Or even looking iteratively at each medium size bins sCurrentMediumPointer = (AllocStructure *)(((unsigned char *)ptr) + alignedSize); sCurrentMediumSize -= alignedSize; return (ptr); } // The buffer cannot be used anymore (smaller than the asked size) // It certainly is a small size buffer now too ;) // Clear the cache and look at the next medium buffer... ReconcileMediumCache(); } { unsigned char * currentAlloc = sCurrentAllocPointer; unsigned char * endAlloc = currentAlloc + alignedSize; if (endAlloc < sEndMainBuffer) { // We have enough memory to allocate sCurrentAllocPointer = endAlloc; // Second most common case (if we are not running out of memory quickly) // Cost if size > SMALL_SIZE_BIN: 2 tests, 2 operations, 1 read, 1 write // // Cost if size <= SMALL_SIZE_BIN: 3 tests, 3 operations, 2 reads, 1 write return (currentAlloc); } } // A bit more optimized for small size... int topBit = SMALL_SIZE_SHIFT; do { ptr = sMediumBin[topBit]; if (ptr != NULL) { // We found a block that should have enough size... break; } ++topBit; } while (topBit < 32); if (ptr != NULL) { CROSSNET_ASSERT(ptr->mSize >= size, ""); // Good, we found a free block of the good size! // Remove it from the free list, move the next free block at the top sMediumBin[topBit] = ptr->mNext; int deltaSize = ptr->mSize - alignedSize; CROSSNET_ASSERT(deltaSize >= 0, ""); // The free block should be at least as big as the allocation we are looking for CROSSNET_ASSERT(ptr->mSize >= (1 << topBit), ""); // The block should be bigger than the corresponding top bit CROSSNET_ASSERT(IsAligned(deltaSize), ""); // Because we are allocating for a small size and we look inside the medium bin // We know that we are going to have left over room... // This will increase memory fragmentation AllocStructure * newFreeBlock = (AllocStructure *)(((unsigned char *)ptr) + alignedSize); // Put this medium buffer in the cache for next time... sCurrentMediumPointer = newFreeBlock; sCurrentMediumSize = deltaSize; // No need to free the block... return (ptr); } } else { // Bigger than small size bin int alignedSize = Align(size); { unsigned char * currentAlloc = sCurrentAllocPointer; unsigned char * endAlloc = currentAlloc + alignedSize; if (endAlloc < sEndMainBuffer) { // We have enough memory to allocate sCurrentAllocPointer = endAlloc; // Second most common case (if we are not running out of memory quickly) // Cost if size > SMALL_SIZE_BIN: 2 tests, 2 operations, 1 read, 1 write // // Cost if size <= SMALL_SIZE_BIN: 3 tests, 3 operations, 2 reads, 1 write return (currentAlloc); } } // In that case we have to update the medium bin with the cached medium pointer (if there is one) SafeReconcileMediumCache(); int topBit = TopBit(NextPowerOf2(size)); do { ptr = sMediumBin[topBit]; if (ptr != NULL) { // We found a block that should have enough size... break; } ++topBit; } while (topBit < 32); if (ptr != NULL) { CROSSNET_ASSERT(ptr->mSize >= size, ""); // Good, we found a free block of the good size! // Remove it from the free list, move the next free block at the top sMediumBin[topBit] = ptr->mNext; int deltaSize = ptr->mSize - alignedSize; CROSSNET_ASSERT(deltaSize >= 0, ""); // The free block should be at least as big as the allocation we are looking for CROSSNET_ASSERT(ptr->mSize >= (1 << topBit), ""); // The block should be bigger than the corresponding top bit CROSSNET_ASSERT(IsAligned(deltaSize), ""); if (deltaSize > 0) { // It means that there is some left over from the block // We either have to push it on the small bin or on the medium bin // This will increase memory fragmentation AllocStructure * newFreeBlock = (AllocStructure *)(((unsigned char *)ptr) + alignedSize); InternalFree(newFreeBlock, deltaSize); } return (ptr); } } /* { unsigned char * currentAlloc = sCurrentAllocPointer; unsigned char * endAlloc = currentAlloc + alignedSize; if (endAlloc < sEndMainBuffer) { // We have enough memory to allocate sCurrentAllocPointer = endAlloc; // Second most common case (if we are not running out of memory quickly) // Cost if size > SMALL_SIZE_BIN: 2 tests, 2 operations, 1 read, 1 write // // Cost if size <= SMALL_SIZE_BIN: 3 tests, 3 operations, 2 reads, 1 write return (currentAlloc); } } */ // Let's recapitulate, we did not find a free block in: // 1. The Small Object Allocator (recycled small objects) // 2. At the end of the main buffer (for new objects) // 3. In the medium allocator (recycled medium and big objects). if (afterGC) { // We did a GC already with no luck... // let's try with the last user allocator AllocateFunctionPointer func = ::CrossNetRuntime::GetOptions().mAllocateAfterGCCallback; if (func != NULL) { return (func(size)); } // No function pointer set, can't allocate return (NULL); } else { // Before we collect, ask the user what he wants to do AllocateFunctionPointer func = ::CrossNetRuntime::GetOptions().mAllocateBeforeGCCallback; if (func != NULL) { // He provided a callback, let's use it void * result = func(size); if (result != NULL) { // And the callback allocated something! return (result); } } } CROSSNET_ASSERT(afterGC == false, "Can only before collect here!"); // So we are before collect and everything failed, // Even the user didn't provide an allocation or were not able to allocate more // The only remaining thing to do is to Garbage Collect, // hoping it will free some memory... GCManager::Collect(GCManager::MAX_GENERATION, false); // Recurse the same function again, this time stating that the GC has been done already // This won't be done more often... return (Allocate(size, true)); }
void glf_resize(gl_tex_font_p glf, uint16_t font_size) { if((glf != NULL) && (glf->ft_face != NULL)) { const GLint padding = 2; GLubyte *buffer; GLint chars_in_row, chars_in_column; size_t buffer_size; int x, y, xx, yy; int i, ii, i0 = 0; // clear old atlas, if exists if(glf->gl_tex_indexes != NULL) { if(glf->gl_tex_indexes_count > 0) { qglDeleteTextures(glf->gl_tex_indexes_count, glf->gl_tex_indexes); } free(glf->gl_tex_indexes); } glf->gl_tex_indexes = NULL; glf->gl_real_tex_indexes_count = 0; // resize base font glf->font_size = font_size; FT_Set_Char_Size(glf->ft_face, font_size << 6, font_size << 6, 0, 0); // calculate texture atlas size chars_in_row = 1 + sqrt(glf->glyphs_count); glf->gl_tex_width = (font_size + padding) * chars_in_row; glf->gl_tex_width = NextPowerOf2(glf->gl_tex_width); if(glf->gl_tex_width > glf->gl_max_tex_width) { glf->gl_tex_width = glf->gl_max_tex_width; } // create new atlas chars_in_row = glf->gl_tex_width / (font_size + padding); chars_in_column = glf->glyphs_count / chars_in_row + 1; glf->gl_tex_indexes_count = (chars_in_column * (font_size + padding)) / glf->gl_tex_width + 1; glf->gl_tex_indexes = (GLuint*)malloc(glf->gl_tex_indexes_count * sizeof(GLuint)); qglGenTextures(glf->gl_tex_indexes_count, glf->gl_tex_indexes); buffer_size = glf->gl_tex_width * glf->gl_tex_width * sizeof(GLubyte); buffer = (GLubyte*)malloc(buffer_size); memset(buffer, 0x00, buffer_size); for(i = 0, x = 0, y = 0; i < glf->glyphs_count; i++) { FT_GlyphSlot g; glf->glyphs[i].tex_index = 0; /* load glyph image into the slot (erase previous one) */ if(FT_Load_Glyph(glf->ft_face, i, FT_LOAD_RENDER)) { continue; } /* convert to an anti-aliased bitmap */ if(FT_Render_Glyph(((FT_Face)glf->ft_face)->glyph, FT_RENDER_MODE_NORMAL)) { continue; } g = ((FT_Face)glf->ft_face)->glyph; glf->glyphs[i].width = g->bitmap.width; glf->glyphs[i].height = g->bitmap.rows; glf->glyphs[i].advance_x_pt = g->advance.x; glf->glyphs[i].advance_y_pt = g->advance.y; glf->glyphs[i].left = g->bitmap_left; glf->glyphs[i].top = g->bitmap_top; if((g->bitmap.width == 0) || (g->bitmap.rows == 0)) { continue; } if(x + g->bitmap.width > glf->gl_tex_width) { x = 0; y += glf->font_size + padding; if(y + glf->font_size > glf->gl_tex_width) { int ii; qglBindTexture(GL_TEXTURE_2D, glf->gl_tex_indexes[glf->gl_real_tex_indexes_count]); qglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); qglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, glf->gl_tex_width, glf->gl_tex_width, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer); for(ii = i0; ii < i; ii++) { glf->glyphs[ii].tex_x0 /= (GLfloat)glf->gl_tex_width; glf->glyphs[ii].tex_x1 /= (GLfloat)glf->gl_tex_width; glf->glyphs[ii].tex_y0 /= (GLfloat)glf->gl_tex_width; glf->glyphs[ii].tex_y1 /= (GLfloat)glf->gl_tex_width; } memset(buffer, 0x00, buffer_size); y = 0; i0 = i; glf->gl_real_tex_indexes_count++; } } glf->glyphs[i].tex_x0 = (GLfloat)x; glf->glyphs[i].tex_y0 = (GLfloat)y; glf->glyphs[i].tex_x1 = (GLfloat)(x + g->bitmap.width); glf->glyphs[i].tex_y1 = (GLfloat)(y + g->bitmap.rows); glf->glyphs[i].tex_index = glf->gl_tex_indexes[glf->gl_real_tex_indexes_count]; for(xx = 0; xx < g->bitmap.width; xx++) { for(yy = 0; yy < g->bitmap.rows; yy++) { buffer[(y+yy)*glf->gl_tex_width + (x+xx)] = g->bitmap.buffer[yy * g->bitmap.width + xx]; } } x += (g->bitmap.width + padding); } qglBindTexture(GL_TEXTURE_2D, glf->gl_tex_indexes[glf->gl_real_tex_indexes_count]); qglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); chars_in_column = NextPowerOf2(y + font_size + padding); qglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, glf->gl_tex_width, chars_in_column, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer); for(ii = i0; ii < glf->glyphs_count; ii++) { glf->glyphs[ii].tex_x0 /= (GLfloat)glf->gl_tex_width; glf->glyphs[ii].tex_x1 /= (GLfloat)glf->gl_tex_width; glf->glyphs[ii].tex_y0 /= (GLfloat)chars_in_column; glf->glyphs[ii].tex_y1 /= (GLfloat)chars_in_column; } free(buffer); glf->gl_real_tex_indexes_count++; } }
/*! * Lays out the texture data and switches the atlas to laid out mode. This makes * use of a bsp_tree_2d to handle all the really annoying stuff. */ void bordered_texture_atlas::layOutTextures() { // First step: Sort the canonical textures by size. unsigned long *sorted_indices = new unsigned long[number_canonical_object_textures]; for (unsigned long i = 0; i < number_canonical_object_textures; i++) sorted_indices[i] = i; compare_context = this; qsort(sorted_indices, number_canonical_object_textures, sizeof(sorted_indices[0]), compareCanonicalTextureSizes); compare_context = NULL; // Find positions for the canonical textures number_result_pages = 0; result_page_height = NULL; bsp_tree_2d_p *result_pages = NULL; for (unsigned long texture = 0; texture < number_canonical_object_textures; texture++) { struct canonical_object_texture &canonical = canonical_object_textures[sorted_indices[texture]]; // Try to find space in an existing page. bool found_place = 0; for (unsigned long page = 0; page < number_result_pages; page++) { found_place = BSPTree2D_FindSpaceFor(result_pages[page], canonical.width + 2*border_width, canonical.height + 2*border_width, &(canonical.new_x_with_border), &(canonical.new_y_with_border)); if (found_place) { canonical.new_page = page; unsigned highest_y = canonical.new_y_with_border + canonical.height + border_width * 2; if (highest_y + 1 > result_page_height[page]) result_page_height[page] = highest_y; break; } } // No existing page has enough remaining space so open new one. if (!found_place) { number_result_pages += 1; result_pages = (bsp_tree_2d_p *) realloc(result_pages, sizeof(bsp_tree_2d_p) * number_result_pages); result_pages[number_result_pages - 1] = BSPTree2D_Create(result_page_width, result_page_width); result_page_height = (unsigned *) realloc(result_page_height, sizeof(unsigned) * number_result_pages); BSPTree2D_FindSpaceFor(result_pages[number_result_pages - 1], canonical.width + 2*border_width, canonical.height + 2*border_width, &(canonical.new_x_with_border), &(canonical.new_y_with_border)); canonical.new_page = number_result_pages - 1; unsigned highest_y = canonical.new_y_with_border + canonical.height + border_width * 2; result_page_height[number_result_pages - 1] = highest_y; } } // Fix up heights if necessary for (unsigned page = 0; page < number_result_pages; page++) { result_page_height[page] = NextPowerOf2(result_page_height[page]); } // Cleanup delete [] sorted_indices; for (unsigned long i = 0; i < number_result_pages; i++) BSPTree2D_Destroy(result_pages[i]); free(result_pages); }
TEST_F(HashTableTest, CanInsertDuplicateKeys) { codegen::util::HashTable table{GetMemPool(), sizeof(Key), sizeof(Value)}; constexpr uint32_t to_insert = 50000; constexpr uint32_t c1 = 4444; constexpr uint32_t max_dups = 4; std::vector<Key> keys; // Insert keys uint32_t num_inserts = 0; for (uint32_t i = 0; i < to_insert; i++) { // Choose a random number of duplicates to insert. Store this in the k1. uint32_t num_dups = 1 + (rand() % max_dups); Key k{num_dups, i}; // Duplicate insertion for (uint32_t dup = 0; dup < num_dups; dup++) { Value v = {.v1 = k.k2, .v2 = 2, .v3 = 3, .v4 = c1}; table.TypedInsert(k.Hash(), k, v); num_inserts++; } keys.emplace_back(k); } EXPECT_EQ(num_inserts, table.NumElements()); // Lookup for (const auto &key : keys) { uint32_t count = 0; std::function<void(const Value &v)> f = [&key, &count, &c1](const Value &v) { EXPECT_EQ(key.k2, v.v1) << "Value's [v1] found in table doesn't match insert key"; EXPECT_EQ(c1, v.v4) << "Value's [v4] doesn't match constant"; count++; }; table.TypedProbe(key.Hash(), key, f); EXPECT_EQ(key.k1, count) << key << " found " << count << " dups ..."; } } TEST_F(HashTableTest, CanInsertLazilyWithDups) { codegen::util::HashTable table{GetMemPool(), sizeof(Key), sizeof(Value)}; constexpr uint32_t to_insert = 50000; constexpr uint32_t c1 = 4444; constexpr uint32_t max_dups = 4; std::vector<Key> keys; // Insert keys uint32_t num_inserts = 0; for (uint32_t i = 0; i < to_insert; i++) { // Choose a random number of duplicates to insert. Store this in the k1. uint32_t num_dups = 1 + (rand() % max_dups); Key k{num_dups, i}; // Duplicate insertion for (uint32_t dup = 0; dup < num_dups; dup++) { Value v = {.v1 = k.k2, .v2 = 2, .v3 = 3, .v4 = c1}; table.TypedInsertLazy(k.Hash(), k, v); num_inserts++; } keys.emplace_back(k); } // Number of elements should reflect lazy insertions EXPECT_EQ(num_inserts, table.NumElements()); EXPECT_LT(table.Capacity(), table.NumElements()); // Build lazy table.BuildLazy(); // Lookups should succeed for (const auto &key : keys) { uint32_t count = 0; std::function<void(const Value &v)> f = [&key, &count, &c1](const Value &v) { EXPECT_EQ(key.k2, v.v1) << "Value's [v1] found in table doesn't match insert key"; EXPECT_EQ(c1, v.v4) << "Value's [v4] doesn't match constant"; count++; }; table.TypedProbe(key.Hash(), key, f); EXPECT_EQ(key.k1, count) << key << " found " << count << " dups ..."; } } TEST_F(HashTableTest, ParallelMerge) { constexpr uint32_t num_threads = 4; constexpr uint32_t to_insert = 20000; // Allocate hash tables for each thread executor::ExecutorContext exec_ctx{nullptr}; auto &thread_states = exec_ctx.GetThreadStates(); thread_states.Reset(sizeof(codegen::util::HashTable)); thread_states.Allocate(num_threads); // The keys we insert std::mutex keys_mutex; std::vector<Key> keys; // The global hash table codegen::util::HashTable global_table{*exec_ctx.GetPool(), sizeof(Key), sizeof(Value)}; auto add_key = [&keys_mutex, &keys](const Key &k) { std::lock_guard<std::mutex> lock{keys_mutex}; keys.emplace_back(k); }; // Insert function auto insert_fn = [&add_key, &exec_ctx](uint64_t tid) { // Get the local table for this thread auto *table = reinterpret_cast<codegen::util::HashTable *>( exec_ctx.GetThreadStates().AccessThreadState(tid)); // Initialize it codegen::util::HashTable::Init(*table, exec_ctx, sizeof(Key), sizeof(Value)); // Insert keys disjoint from other threads for (uint32_t i = tid * to_insert, end = i + to_insert; i != end; i++) { Key k{static_cast<uint32_t>(tid), i}; Value v = {.v1 = k.k2, .v2 = k.k1, .v3 = 3, .v4 = 4444}; table->TypedInsertLazy(k.Hash(), k, v); add_key(k); } }; auto merge_fn = [&global_table, &thread_states](uint64_t tid) { // Get the local table for this threads auto *table = reinterpret_cast<codegen::util::HashTable *>( thread_states.AccessThreadState(tid)); // Merge it into the global table global_table.MergeLazyUnfinished(*table); }; // First insert into thread local tables in parallel LaunchParallelTest(num_threads, insert_fn); for (uint32_t tid = 0; tid < num_threads; tid++) { auto *ht = reinterpret_cast<codegen::util::HashTable *>( thread_states.AccessThreadState(tid)); EXPECT_EQ(to_insert, ht->NumElements()); } // Now resize global table global_table.ReserveLazy(thread_states, 0); EXPECT_EQ(NextPowerOf2(keys.size()), global_table.Capacity()); // Now merge thread-local tables into global table in parallel LaunchParallelTest(num_threads, merge_fn); // Clean up local tables for (uint32_t tid = 0; tid < num_threads; tid++) { auto *table = reinterpret_cast<codegen::util::HashTable *>( thread_states.AccessThreadState(tid)); codegen::util::HashTable::Destroy(*table); } // Now probe global EXPECT_EQ(to_insert * num_threads, global_table.NumElements()); EXPECT_LE(global_table.NumElements(), global_table.Capacity()); for (const auto &key : keys) { uint32_t count = 0; std::function<void(const Value &v)> f = [&key, &count](const Value &v) { EXPECT_EQ(key.k2, v.v1) << "Value's [v1] found in table doesn't match insert key"; EXPECT_EQ(key.k1, v.v2) << "Key " << key << " inserted by thread " << key.k1 << " but value was inserted by thread " << v.v2; count++; }; global_table.TypedProbe(key.Hash(), key, f); EXPECT_EQ(1, count) << "Found duplicate keys in unique key test"; } } } // namespace test
inline FTPoint FTBufferFontImpl::RenderI(const T* string, const int len, FTPoint position, FTPoint spacing, int renderMode) { const float padding = 3.0f; int width, height, texWidth, texHeight; int cacheIndex = -1; bool inCache = false; // Protect blending functions, GL_BLEND and GL_TEXTURE_2D glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT); // Protect glPixelStorei() calls glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); glEnable(GL_TEXTURE_2D); if ((renderMode & FTGL::RENDER_NOBLEND) == 0) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE } // Search whether the string is already in a texture we uploaded for(int n = 0; n < BUFFER_CACHE_SIZE; n++) { int i = (lastString + n + BUFFER_CACHE_SIZE) % BUFFER_CACHE_SIZE; if(stringCache[i] && !StringCompare(stringCache[i], string, len)) { cacheIndex = i; inCache = true; break; } } // If the string was not found, we need to put it in the cache and compute // its new bounding box. if(!inCache) { // FIXME: this cache is not very efficient. We should first expire // strings that are not used very often. cacheIndex = lastString; lastString = (lastString + 1) % BUFFER_CACHE_SIZE; if(stringCache[cacheIndex]) { free(stringCache[cacheIndex]); } // FIXME: only the first N bytes are copied; we want the first N chars. stringCache[cacheIndex] = StringCopy(string, len); bboxCache[cacheIndex] = BBox(string, len, FTPoint(), spacing); } FTBBox bbox = bboxCache[cacheIndex]; width = static_cast<int>(bbox.Upper().X() - bbox.Lower().X() + padding + padding + 0.5); height = static_cast<int>(bbox.Upper().Y() - bbox.Lower().Y() + padding + padding + 0.5); texWidth = NextPowerOf2(width); texHeight = NextPowerOf2(height); glBindTexture(GL_TEXTURE_2D, idCache[cacheIndex]); // If the string was not found, we need to render the text in a new // texture buffer, then upload it to the OpenGL layer. if(!inCache) { buffer->Size(texWidth, texHeight); buffer->Pos(FTPoint(padding, padding) - bbox.Lower()); advanceCache[cacheIndex] = FTFontImpl::Render(string, len, FTPoint(), spacing, renderMode); glBindTexture(GL_TEXTURE_2D, idCache[cacheIndex]); glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* TODO: use glTexSubImage2D later? */ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texWidth, texHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, (GLvoid *)buffer->Pixels()); buffer->Size(0, 0); } FTPoint low = position + bbox.Lower(); FTPoint up = position + bbox.Upper(); glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(padding / texWidth, (texHeight - height + padding) / texHeight); glVertex2f(low.Xf(), up.Yf()); glTexCoord2f(padding / texWidth, (texHeight - padding) / texHeight); glVertex2f(low.Xf(), low.Yf()); glTexCoord2f((width - padding) / texWidth, (texHeight - padding) / texHeight); glVertex2f(up.Xf(), low.Yf()); glTexCoord2f((width - padding) / texWidth, (texHeight - height + padding) / texHeight); glVertex2f(up.Xf(), up.Yf()); glEnd(); glPopClientAttrib(); glPopAttrib(); return position + advanceCache[cacheIndex]; }