MetalUniformStreaming/AAPLPlasmaUniforms.mm

/*
 Copyright (C) 2014 Apple Inc. All Rights Reserved.
 See LICENSE.txt for this sample’s licensing information
 
 */
 
#import <string>
 
#import "AAPLPlasmaTypes.h"
#import "AAPLTransforms.h"
#import "AAPLPlasmaParams.h"
#import "AAPLPlasmaUniforms.h"
 
namespace AAPL
{
    namespace Plasma
    {
        static const uint32_t kCntVertUniformBuffer = 2;
        static const uint32_t kSzVertUniformBuffer  = kCntVertUniformBuffer * kSzVertUniforms;
        
        static const uint32_t kCntFragUniformBuffer = 2;
        static const uint32_t kSzFragUniformBuffer  = kCntFragUniformBuffer * kSzFragUniforms;
        
        static const uint32_t kMaxBufferBytesPerFrame = kSzVertUniformBuffer + kSzFragUniformBuffer;
        
        static const NSUInteger kMaxEncodes = 2;
        
        static const float kFOVY          = 65.0f;
        static const float kRotationDelta = 2.0f;
        
        static const simd::float3 kEye    = {0.0f, 0.0f, 0.0f};
        static const simd::float3 kCenter = {0.0f, 0.0f, 1.0f};
        static const simd::float3 kUp     = {0.0f, 1.0f, 0.0f};
    } // Plasma
} // AAPL
 
@implementation AAPLPlasmaUniforms
{
@private
    AAPL::Plasma::Params              m_Plasma[2];
    AAPL::Plasma::Uniforms::Vertex    m_VertUniforms[2];
    AAPL::Plasma::Uniforms::Fragment  m_FragUniforms[2];
    AAPL::Plasma::Transforms          m_Transforms;
    
    UIInterfaceOrientation  m_Orientation[2];
    
    NSMutableArray  *mpUniformBuffer;
    
    NSUInteger  mnCapacity;
    NSUInteger  mnMemBarrierIndex;
    NSUInteger  mnEncodeIndex;
    
    CGRect  _bounds;
}
 
@synthesize bounds;
 
- (instancetype) initWithDevice:(id <MTLDevice>)device
                       capacity:(NSUInteger)capacity
{
    self = [super init];
    
    if(self)
    {
        if(device)
        {
            // Set the total number of inflight buffers
            mnCapacity = capacity;
            
            // Create a mutable array to hold all the constant buffers
            mpUniformBuffer = [[NSMutableArray alloc] initWithCapacity:mnCapacity];
            
            if(!mpUniformBuffer)
            {
                NSLog(@">> ERROR: Failed creating a mutable array for uniform buffers!");
                
                return nil;
            } // if
            
            // allocate one region of memory for the constant buffer per max in
            // flight command buffers so that memory is properly syncronized.
            uint32_t i;
            
            id <MTLBuffer> buffer = nil;
            
            for(i = 0; i < mnCapacity; ++i)
            {
                // Create a new constant buffer
                buffer = [device newBufferWithLength:AAPL::Plasma::kMaxBufferBytesPerFrame
                                             options:0];
                
                
                if(!buffer)
                {
                    NSLog(@">> ERROR: Failed creating a new buffer[%d]!",i);
                    
                    break;
                } // if
                
                // Set a label for the constant buffer
                buffer.label = [NSString stringWithFormat:@"PlasmaConstantBuffer%i", i];
                
                // Add the constant buffer to the mutable array
                [mpUniformBuffer addObject:buffer];
            } // for
            
            if([mpUniformBuffer count] != mnCapacity)
            {
                NSLog(@">> ERROR: Failed creating all the requested buffers!");
                
                return nil;
            } // if
            
            std::memset(m_VertUniforms, 0x0, AAPL::Plasma::kSzVertUniformBuffer);
            std::memset(m_FragUniforms, 0x0, AAPL::Plasma::kSzFragUniformBuffer);
            
            m_FragUniforms[0].mnType = 1;
            m_FragUniforms[1].mnType = 2;
            
            m_Transforms.mnAspect   = 0.0f;
            m_Transforms.mnRotation = 0.0f;
            m_Transforms.mnFOVY     = AAPL::Plasma::kFOVY;
            
            m_Transforms.m_View      = AAPL::Math::lookAt(AAPL::Plasma::kEye, AAPL::Plasma::kCenter, AAPL::Plasma::kUp);
            m_Transforms.m_Model     = AAPL::Math::translate(0.0f, 0.0f, 7.0f);
            m_Transforms.m_ModelView = AAPL::Math::translate(0.0f, 0.0f, 1.5f);
            
            mnMemBarrierIndex = 0;
            mnEncodeIndex     = 0;
        } // if
    } // if
    
    return self;
} // initWithDevice
 
- (void) dealloc
{
    mpUniformBuffer = nil;
} // dealloc
 
- (BOOL) upload
{
    // Get the constant bufferm at index
    id <MTLBuffer> buffer = mpUniformBuffer[mnMemBarrierIndex];
    
    if(!buffer)
    {
        NSLog(@">> ERROR: Failed to get the constant buffer[%lu]!",mnMemBarrierIndex);
        
        return NO;
    } // if
    
    // Get the base address of the constant buffer
    uint8_t *pBufferPointer = (uint8_t *)[buffer contents];
    
    if(!pBufferPointer)
    {
        NSLog(@">> ERROR: Failed to get the constant buffer[%lu] pointer!",mnMemBarrierIndex);
        
        return NO;
    } // if
    
    // Copy the updated linear transformations for both cubes into the constant vertex buffer
    std::memcpy(pBufferPointer, m_VertUniforms, AAPL::Plasma::kSzVertUniformBuffer);
    
    // Increment the buffer pointer to where we can write fragment constant data
    pBufferPointer += AAPL::Plasma::kSzVertUniformBuffer;
    
    // Copy scale and time factors for both cubes into the constant fragment buffer
    std::memcpy(pBufferPointer, m_FragUniforms, AAPL::Plasma::kSzFragUniformBuffer);
    
    return YES;
} // upload
 
- (void) _encode:(id <MTLRenderCommandEncoder>)renderEncoder
          offset:(AAPL::Offset)offset
{
    [renderEncoder setVertexBuffer:mpUniformBuffer[mnMemBarrierIndex]
                            offset:offset.x
                           atIndex:3 ];
    
    [renderEncoder setFragmentBuffer:mpUniformBuffer[mnMemBarrierIndex]
                              offset:AAPL::Plasma::kSzVertUniformBuffer + offset.y
                             atIndex:0 ];
} // _encode
 
- (void) encode:(id <MTLRenderCommandEncoder>)renderEncoder
{
    AAPL::Offset offset = {0, 0};
    
    if(mnEncodeIndex == 0)
    {
        [self _encode:renderEncoder
               offset:offset];
    }
    else if(mnEncodeIndex == 1)
    {
        offset.x = AAPL::Plasma::kSzVertUniforms;
        offset.y = AAPL::Plasma::kSzFragUniforms;
        
        [self _encode:renderEncoder
               offset:offset];
        
        // Increment the memory barrier index
        mnMemBarrierIndex = (mnMemBarrierIndex + 1) % mnCapacity;
    }
    
    mnEncodeIndex = (mnEncodeIndex + 1) % AAPL::Plasma::kMaxEncodes;
} // encode
 
- (void) setBounds:(CGRect)inBounds
{
    // To correctly compute the aspect ratio determine the device
    // interface orientation.
    m_Orientation[0] = [UIApplication sharedApplication].statusBarOrientation;
    
    if(m_Orientation[0] != m_Orientation[1])
    {
        // Set the bounds
        _bounds = inBounds;
        
        // Get the bounds for the current rendering layer
        m_Transforms.mnAspect     = std::abs(_bounds.size.width / _bounds.size.height);
        m_Transforms.m_Projection = AAPL::Math::perspective_fov(m_Transforms.mnFOVY, m_Transforms.mnAspect, 0.1f, 100.0f);
        
        m_VertUniforms[0].m_Projection = m_Transforms.m_Projection;
        m_VertUniforms[1].m_Projection = m_Transforms.m_Projection;
        
        m_Orientation[1] = m_Orientation[0];
    } // if
} // setBounds
 
- (void) update
{
    simd::float4x4 rotate        = AAPL::Math::rotate(m_Transforms.mnRotation, 0.0f, 1.0f, 0.0f);
    simd::float4x4 model         = m_Transforms.m_Model * rotate;
    simd::float4x4 modelViewBase = m_Transforms.m_View * model;
    
    rotate = AAPL::Math::rotate(m_Transforms.mnRotation, 1.0f, 1.0f, 1.0f);
    
    simd::float4x4 modelView = m_Transforms.m_ModelView * rotate;
    
    modelView = modelViewBase * modelView;
    
    // Set the mode-view matrix for the primary vertex constant buffer
    m_VertUniforms[0].m_ModelView = modelView;
    
    modelView = AAPL::Math::translate(0.0f, 0.0f, -1.5f);
    modelView = modelView * rotate;
    modelView = modelViewBase * modelView;
    
    // Set the mode-view matrix for the second vertex constant buffer
    m_VertUniforms[1].m_ModelView = modelView;
    
    // update rotation
    m_Transforms.mnRotation += AAPL::Plasma::kRotationDelta;
    
    // Update the plasma animation
    AAPL::Vector2 v = m_Plasma[0].update();
    
    m_FragUniforms[0].mnTime  = v.time;
    m_FragUniforms[0].mnScale = v.scale;
    
    AAPL::Vector2 w = m_Plasma[1].update();
    
    m_FragUniforms[1].mnTime  = w.time;
    m_FragUniforms[1].mnScale = w.scale;
} // update
 
@end