avm2: Add support for Stage3D bytearray/compressed textures (#13180)
This commit is contained in:
parent
7e4e4d4c5b
commit
ec7a8ac645
|
@ -378,6 +378,29 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.68.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.37",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
|
@ -848,7 +871,7 @@ version = "0.2.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f034b2258e6c4ade2f73bf87b21047567fb913ee9550837c2316d139b0262b24"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"bindgen 0.64.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2483,6 +2506,17 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
||||
|
||||
[[package]]
|
||||
name = "jpegxr"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/ruffle-rs/jpegxr?branch=ruffle#0251753f3ea4b7e301cb89e92c5707055b1db501"
|
||||
dependencies = [
|
||||
"bindgen 0.68.1",
|
||||
"cc",
|
||||
"libc",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.64"
|
||||
|
@ -3537,6 +3571,16 @@ dependencies = [
|
|||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primal-check"
|
||||
version = "0.3.3"
|
||||
|
@ -3875,8 +3919,10 @@ dependencies = [
|
|||
"futures",
|
||||
"generational-arena",
|
||||
"hashbrown 0.14.0",
|
||||
"image",
|
||||
"indexmap 2.0.0",
|
||||
"instant",
|
||||
"jpegxr",
|
||||
"linkme",
|
||||
"lzma-rs",
|
||||
"nellymoser-rs",
|
||||
|
@ -5560,6 +5606,17 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
|
||||
dependencies = [
|
||||
"either",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wide"
|
||||
version = "0.7.11"
|
||||
|
|
|
@ -56,6 +56,8 @@ egui_extras = { git = "https://github.com/emilk/egui", rev = "98087029e020a1b2d7
|
|||
png = { version = "0.17.10", optional = true }
|
||||
flv-rs = { path = "../flv" }
|
||||
async-channel = "1.9.0"
|
||||
jpegxr = { git = "https://github.com/ruffle-rs/jpegxr", branch = "ruffle" }
|
||||
image = { version = "0.24.7", default-features = false, features = ["tiff"] }
|
||||
|
||||
[target.'cfg(not(target_family = "wasm"))'.dependencies.futures]
|
||||
version = "0.3.28"
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package flash.display3D.textures {
|
||||
import flash.display.BitmapData;
|
||||
import flash.utils.ByteArray;
|
||||
import __ruffle__.stub_method;
|
||||
|
||||
public final class CubeTexture extends TextureBase {
|
||||
public native function uploadFromBitmapData(source:BitmapData, side:uint, miplevel:uint = 0):void
|
||||
public native function uploadFromBitmapData(source:BitmapData, side:uint, miplevel:uint = 0):void;
|
||||
public native function uploadFromByteArray(data:ByteArray, byteArrayOffset:uint, side:uint, miplevel:uint = 0);
|
||||
public native function uploadCompressedTextureFromByteArray(data:ByteArray, byteArrayOffset:uint, async:Boolean = false):void;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
package flash.display3D.textures {
|
||||
import flash.display.BitmapData;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
public final class RectangleTexture extends TextureBase {
|
||||
public native function uploadFromBitmapData(source:BitmapData):void
|
||||
public native function uploadFromBitmapData(source:BitmapData):void;
|
||||
public native function uploadFromByteArray(data:ByteArray, byteArrayOffset:uint):void;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
package flash.display3D.textures {
|
||||
import flash.display.BitmapData;
|
||||
import flash.utils.ByteArray;
|
||||
import __ruffle__.stub_method;
|
||||
|
||||
public final class Texture extends TextureBase {
|
||||
public native function uploadFromBitmapData(source:BitmapData, miplevel:uint = 0):void
|
||||
public native function uploadFromBitmapData(source:BitmapData, miplevel:uint = 0):void;
|
||||
public native function uploadFromByteArray(data:ByteArray, byteArrayOffset:uint, miplevel:uint = 0):void;
|
||||
public native function uploadCompressedTextureFromByteArray(data:ByteArray, byteArrayOffset:uint, async:Boolean = false):void;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,82 @@
|
|||
use ruffle_render::backend::Context3DTextureFormat;
|
||||
|
||||
use crate::avm2::globals::flash::display3D::textures::texture::do_compressed_upload;
|
||||
use crate::avm2::parameters::ParametersExt;
|
||||
use crate::avm2::Activation;
|
||||
use crate::avm2::TObject;
|
||||
use crate::avm2::Value;
|
||||
use crate::avm2::{Error, Object};
|
||||
use crate::avm2_stub_method;
|
||||
|
||||
use super::texture::do_copy;
|
||||
|
||||
pub fn upload_from_byte_array<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
// This should work, but it's currently untested
|
||||
avm2_stub_method!(
|
||||
activation,
|
||||
"flash.display3D.textures.CubeTexture",
|
||||
"uploadFromByteArray"
|
||||
);
|
||||
let texture = this.as_texture().unwrap();
|
||||
let data = args.get_object(activation, 0, "data")?;
|
||||
let byte_array_offset = args.get_u32(activation, 1)?;
|
||||
let side = args.get_u32(activation, 2)?;
|
||||
let mip_level = args.get_u32(activation, 3)?;
|
||||
|
||||
do_copy(
|
||||
activation,
|
||||
data,
|
||||
texture,
|
||||
byte_array_offset,
|
||||
side,
|
||||
mip_level,
|
||||
)?;
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn upload_compressed_texture_from_byte_array<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
// This should work, but it's currently untested
|
||||
avm2_stub_method!(
|
||||
activation,
|
||||
"flash.display3D.textures.CubeTexture",
|
||||
"uploadCompressedTextureFromByteArray"
|
||||
);
|
||||
|
||||
let texture = this.as_texture().unwrap();
|
||||
let data = args.get_object(activation, 0, "data")?;
|
||||
let byte_array_offset = args.get_u32(activation, 1)? as usize;
|
||||
let async_ = args.get_bool(2);
|
||||
if async_ {
|
||||
avm2_stub_method!(
|
||||
activation,
|
||||
"flash.display3D.textures.CubeTexture",
|
||||
"uploadCompressedTextureFromByteArray",
|
||||
"with async"
|
||||
);
|
||||
}
|
||||
|
||||
if !matches!(texture.original_format(), Context3DTextureFormat::Bgra) {
|
||||
avm2_stub_method!(
|
||||
activation,
|
||||
"flash.display3D.textures.CubeTexture",
|
||||
"uploadCompressedTextureFromByteArray",
|
||||
"with unsupported format"
|
||||
);
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
do_compressed_upload(activation, texture, data, byte_array_offset, true)?;
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn upload_from_bitmap_data<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
use crate::avm2::parameters::ParametersExt;
|
||||
use crate::avm2::Activation;
|
||||
use crate::avm2::TObject;
|
||||
use crate::avm2::Value;
|
||||
use crate::avm2::{Error, Object};
|
||||
|
||||
use super::texture::do_copy;
|
||||
|
||||
pub fn upload_from_byte_array<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let texture = this.as_texture().unwrap();
|
||||
let data = args.get_object(activation, 0, "data")?;
|
||||
let byte_array_offset = args.get_u32(activation, 1)?;
|
||||
|
||||
do_copy(activation, data, texture, byte_array_offset, 0, 0)?;
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn upload_from_bitmap_data<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
|
|
|
@ -1,8 +1,199 @@
|
|||
use std::io::Cursor;
|
||||
|
||||
use gc_arena::GcCell;
|
||||
use ruffle_render::atf::ATFTexture;
|
||||
use ruffle_render::backend::Context3DTextureFormat;
|
||||
|
||||
use crate::avm2::object::TextureObject;
|
||||
use crate::avm2::parameters::ParametersExt;
|
||||
use crate::avm2::Activation;
|
||||
use crate::avm2::TObject;
|
||||
use crate::avm2::Value;
|
||||
use crate::avm2::{Error, Object};
|
||||
use crate::avm2_stub_method;
|
||||
use crate::bitmap::bitmap_data::BitmapData;
|
||||
use crate::bitmap::bitmap_data::BitmapDataWrapper;
|
||||
use crate::bitmap::bitmap_data::Color;
|
||||
|
||||
pub fn do_copy<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
data: Object<'gc>,
|
||||
texture: TextureObject<'gc>,
|
||||
byte_array_offset: u32,
|
||||
side: u32,
|
||||
mip_level: u32,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
if mip_level != 0 {
|
||||
avm2_stub_method!(
|
||||
activation,
|
||||
"flash.display3D.textures.Texture",
|
||||
"uploadFromByteArray",
|
||||
"with miplevel != 0"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// FIXME - see if we can avoid this intermediate BitmapDataWrapper, and copy
|
||||
// directly from a buffer to the target GPU texture
|
||||
let bitmap_data = match texture.original_format() {
|
||||
Context3DTextureFormat::Bgra => {
|
||||
let width = texture.handle().width();
|
||||
let height = texture.handle().height();
|
||||
|
||||
let bytearray = data.as_bytearray().unwrap();
|
||||
|
||||
let colors: Vec<_> = bytearray
|
||||
.read_at((4 * width * height) as usize, byte_array_offset as usize)
|
||||
.expect("Failed to read")
|
||||
.chunks_exact(4)
|
||||
.map(|chunk| {
|
||||
// The ByteArray is in BGRA format. FIXME - should this be premultiplied?
|
||||
Color::argb(chunk[3], chunk[2], chunk[1], chunk[0])
|
||||
})
|
||||
.collect();
|
||||
|
||||
let bitmap_data = BitmapData::new_with_pixels(width, height, true, colors);
|
||||
BitmapDataWrapper::new(GcCell::new(activation.context.gc_context, bitmap_data))
|
||||
}
|
||||
_ => {
|
||||
tracing::warn!(
|
||||
"uploadFromByteArray with unsupported format: {:?}",
|
||||
texture.original_format()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
texture
|
||||
.context3d()
|
||||
.copy_bitmap_to_texture(bitmap_data.sync(), texture.handle(), side);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn do_compressed_upload<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
texture: TextureObject<'gc>,
|
||||
data: Object<'gc>,
|
||||
byte_array_offset: usize,
|
||||
is_cube: bool,
|
||||
) -> Result<(), Error<'gc>> {
|
||||
let atf_texture =
|
||||
ATFTexture::from_bytes(&data.as_bytearray().unwrap().bytes()[byte_array_offset..])
|
||||
.expect("Failed to parse ATF texture");
|
||||
|
||||
if is_cube != atf_texture.cubemap {
|
||||
return Err("Stage3D Texture and ATF Texture must both be cube/non-cube".into());
|
||||
}
|
||||
|
||||
if atf_texture.width != texture.handle().width()
|
||||
|| atf_texture.height != texture.handle().height()
|
||||
{
|
||||
return Err("ATF texture dimensions do not match Texture dimensions".into());
|
||||
}
|
||||
|
||||
// Just use the first mip level for now. We ignore the builtin format - the JPEG-XR format
|
||||
// appears to override it
|
||||
let mut first_mip = Cursor::new(&atf_texture.face_mip_data[0][0]);
|
||||
let mut decoder =
|
||||
jpegxr::ImageDecode::with_reader(&mut first_mip).expect("Failed to decode JPEG-XR image");
|
||||
|
||||
let pixel_format = decoder
|
||||
.get_pixel_format()
|
||||
.expect("Failed to get pixel format");
|
||||
let (jpeg_width, jpeg_height) = decoder.get_size().expect("Failed to get JPEG-XR size");
|
||||
let jpeg_width = jpeg_width as u32;
|
||||
let jpeg_height = jpeg_height as u32;
|
||||
|
||||
assert_eq!(jpeg_width, atf_texture.width, "Mismatched JPEG-XR width");
|
||||
assert_eq!(jpeg_height, atf_texture.height, "Mismatched JPEG-XR height");
|
||||
|
||||
let info = jpegxr::PixelInfo::from_format(pixel_format);
|
||||
let stride = jpeg_width as usize * info.bits_per_pixel() / 8;
|
||||
let size = stride * jpeg_height as usize;
|
||||
|
||||
// We convert the result to a TIFF - this makes the jpegxr library handle
|
||||
// all of the weird JPEG-XR alpha formats for us. We can then use the normal
|
||||
// `image` crate to decode the TIFF to an rgba array.
|
||||
let mut bmp_buffer = vec![0; size];
|
||||
decoder
|
||||
.convert_to_tiff(&mut Cursor::new(&mut bmp_buffer))
|
||||
.expect("Failed to convert to bitmap");
|
||||
|
||||
let image_reader =
|
||||
image::io::Reader::with_format(Cursor::new(bmp_buffer), image::ImageFormat::Tiff);
|
||||
let bitmap = image_reader
|
||||
.decode()
|
||||
.expect("Failed to decode Bitmap")
|
||||
.to_rgba8();
|
||||
|
||||
// FIXME - are we handling premultiplied alpha correct?
|
||||
let colors: Vec<_> = bitmap
|
||||
.chunks_exact(4)
|
||||
.map(|color| Color::argb(color[3], color[0], color[1], color[2]))
|
||||
.collect();
|
||||
|
||||
let bitmap_data = BitmapData::new_with_pixels(
|
||||
texture.handle().width(),
|
||||
texture.handle().height(),
|
||||
true,
|
||||
colors,
|
||||
);
|
||||
|
||||
let bitmap_data =
|
||||
BitmapDataWrapper::new(GcCell::new(activation.context.gc_context, bitmap_data));
|
||||
|
||||
texture
|
||||
.context3d()
|
||||
.copy_bitmap_to_texture(bitmap_data.sync(), texture.handle(), 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn upload_compressed_texture_from_byte_array<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let texture = this.as_texture().unwrap();
|
||||
let data = args.get_object(activation, 0, "data")?;
|
||||
let byte_array_offset = args.get_u32(activation, 1)? as usize;
|
||||
let async_ = args.get_bool(2);
|
||||
if async_ {
|
||||
avm2_stub_method!(
|
||||
activation,
|
||||
"flash.display3D.textures.Texture",
|
||||
"uploadCompressedTextureFromByteArray",
|
||||
"with async"
|
||||
);
|
||||
}
|
||||
|
||||
if !matches!(texture.original_format(), Context3DTextureFormat::Bgra) {
|
||||
avm2_stub_method!(
|
||||
activation,
|
||||
"flash.display3D.textures.Texture",
|
||||
"uploadCompressedTextureFromByteArray",
|
||||
"with unsupported format"
|
||||
);
|
||||
return Ok(Value::Undefined);
|
||||
}
|
||||
|
||||
do_compressed_upload(activation, texture, data, byte_array_offset, false)?;
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn upload_from_byte_array<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
let texture = this.as_texture().unwrap();
|
||||
let data = args.get_object(activation, 0, "data")?;
|
||||
let byte_array_offset = args.get_u32(activation, 1)?;
|
||||
let mip_level = args.get_u32(activation, 2)?;
|
||||
|
||||
do_copy(activation, data, texture, byte_array_offset, 0, mip_level)?;
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
pub fn upload_from_bitmap_data<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
|
|
|
@ -124,7 +124,7 @@ impl<'gc> Context3DObject<'gc> {
|
|||
})?;
|
||||
|
||||
Ok(Value::Object(TextureObject::from_handle(
|
||||
activation, *self, texture, class,
|
||||
activation, *self, texture, format, class,
|
||||
)?))
|
||||
}
|
||||
|
||||
|
@ -418,7 +418,7 @@ impl<'gc> Context3DObject<'gc> {
|
|||
let class = activation.avm2().classes().cubetexture;
|
||||
|
||||
Ok(Value::Object(TextureObject::from_handle(
|
||||
activation, *self, texture, class,
|
||||
activation, *self, texture, format, class,
|
||||
)?))
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::avm2::Error;
|
|||
use gc_arena::barrier::unlock;
|
||||
use gc_arena::lock::RefLock;
|
||||
use gc_arena::{Collect, Gc, GcWeak, Mutation};
|
||||
use ruffle_render::backend::Texture;
|
||||
use ruffle_render::backend::{Context3DTextureFormat, Texture};
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
|
@ -27,6 +27,7 @@ impl<'gc> TextureObject<'gc> {
|
|||
activation: &mut Activation<'_, 'gc>,
|
||||
context3d: Context3DObject<'gc>,
|
||||
handle: Rc<dyn Texture>,
|
||||
original_format: Context3DTextureFormat,
|
||||
class: ClassObject<'gc>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
let this: Object<'gc> = TextureObject(Gc::new(
|
||||
|
@ -34,6 +35,7 @@ impl<'gc> TextureObject<'gc> {
|
|||
TextureObjectData {
|
||||
base: RefLock::new(ScriptObjectData::new(class)),
|
||||
context3d,
|
||||
original_format,
|
||||
handle,
|
||||
},
|
||||
))
|
||||
|
@ -45,6 +47,10 @@ impl<'gc> TextureObject<'gc> {
|
|||
Ok(this)
|
||||
}
|
||||
|
||||
pub fn original_format(&self) -> Context3DTextureFormat {
|
||||
self.0.original_format
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> Rc<dyn Texture> {
|
||||
self.0.handle.clone()
|
||||
}
|
||||
|
@ -62,6 +68,9 @@ pub struct TextureObjectData<'gc> {
|
|||
|
||||
context3d: Context3DObject<'gc>,
|
||||
|
||||
#[collect(require_static)]
|
||||
original_format: Context3DTextureFormat,
|
||||
|
||||
#[collect(require_static)]
|
||||
handle: Rc<dyn Texture>,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
use std::io::Read;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
pub struct ATFTexture {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub cubemap: bool,
|
||||
pub format: ATFFormat,
|
||||
pub mip_count: u8,
|
||||
// A nested array of `[0..num_faces][0..mip_count]`, where each
|
||||
// entry is the texture data for that mip level and face.
|
||||
pub face_mip_data: Vec<Vec<Vec<u8>>>,
|
||||
}
|
||||
|
||||
#[derive(FromPrimitive, Debug)]
|
||||
pub enum ATFFormat {
|
||||
RGB888 = 0,
|
||||
RGBA8888 = 1,
|
||||
Compressed = 2,
|
||||
RawCompressed = 3,
|
||||
CompressedAlpha = 4,
|
||||
RawCompressedAlpha = 5,
|
||||
CompressedLossy = 0xc,
|
||||
CompressedLossyAlpha = 0xd,
|
||||
}
|
||||
|
||||
impl ATFTexture {
|
||||
pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
// Based on https://github.com/openfl/openfl/blob/develop/src/openfl/display3D/_internal/ATFReader.hx
|
||||
let bytes = &mut bytes;
|
||||
|
||||
let mut string_bytes = [0; 3];
|
||||
bytes.read_exact(&mut string_bytes)?;
|
||||
|
||||
if &string_bytes != b"ATF" {
|
||||
return Err(format!("Invalid ATF signature {string_bytes:?}").into());
|
||||
}
|
||||
|
||||
let version;
|
||||
let _length;
|
||||
|
||||
if bytes[3] == 0xFF {
|
||||
version = bytes[4];
|
||||
*bytes = &bytes[5..];
|
||||
_length = bytes.read_u32::<byteorder::LittleEndian>()?;
|
||||
} else {
|
||||
version = 0;
|
||||
_length = read_uint24(bytes)?;
|
||||
}
|
||||
|
||||
let tdata = bytes.read_u8()?;
|
||||
let cubemap = (tdata >> 7) != 0;
|
||||
|
||||
let format = ATFFormat::from_u8(tdata & 0x7f).ok_or_else(|| {
|
||||
format!(
|
||||
"Invalid ATF format {format} (version {version})",
|
||||
format = tdata & 0x7f,
|
||||
version = version
|
||||
)
|
||||
})?;
|
||||
let width = 1 << bytes.read_u8()?;
|
||||
let height = 1 << bytes.read_u8()?;
|
||||
|
||||
let mip_count = bytes.read_u8()?;
|
||||
let num_faces = if cubemap { 6 } else { 1 };
|
||||
|
||||
let mut face_mip_data = vec![vec![]; num_faces];
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for face in 0..num_faces {
|
||||
for _ in 0..mip_count {
|
||||
// All of the formats consist of a number of (u32_length, data[u32_length]) records.
|
||||
// For now, we just combine them into a single buffer to allow parsing to succeed.
|
||||
let num_records = match format {
|
||||
ATFFormat::RGB888 | ATFFormat::RGBA8888 => 1,
|
||||
ATFFormat::RawCompressed | ATFFormat::RawCompressedAlpha => 4,
|
||||
ATFFormat::Compressed => 11,
|
||||
ATFFormat::CompressedAlpha => {
|
||||
return Err("CompressedAlpha not supported".into());
|
||||
}
|
||||
ATFFormat::CompressedLossy => 12,
|
||||
ATFFormat::CompressedLossyAlpha => 17,
|
||||
};
|
||||
let mut all_data = vec![];
|
||||
for _ in 0..num_records {
|
||||
let len = if version == 0 {
|
||||
read_uint24(bytes)?
|
||||
} else {
|
||||
bytes.read_u32::<BigEndian>()?
|
||||
};
|
||||
let orig_len = all_data.len();
|
||||
all_data.resize(orig_len + len as usize, 0);
|
||||
bytes.read_exact(&mut all_data[orig_len..])?;
|
||||
}
|
||||
face_mip_data[face].push(all_data);
|
||||
}
|
||||
}
|
||||
Ok(ATFTexture {
|
||||
width,
|
||||
height,
|
||||
cubemap,
|
||||
format,
|
||||
mip_count,
|
||||
face_mip_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn read_uint24<R: Read>(data: &mut R) -> Result<u32, Box<dyn std::error::Error>> {
|
||||
let ch1 = data.read_u8()? as u32;
|
||||
let ch2 = data.read_u8()? as u32;
|
||||
let ch3 = data.read_u8()? as u32;
|
||||
Ok(ch1 | (ch2 << 8) | (ch3 << 16))
|
||||
}
|
|
@ -117,7 +117,10 @@ impl_downcast!(VertexBuffer);
|
|||
pub trait ShaderModule: Downcast {}
|
||||
impl_downcast!(ShaderModule);
|
||||
|
||||
pub trait Texture: Downcast {}
|
||||
pub trait Texture: Downcast + Debug {
|
||||
fn width(&self) -> u32;
|
||||
fn height(&self) -> u32;
|
||||
}
|
||||
impl_downcast!(Texture);
|
||||
|
||||
pub trait RawTexture: Downcast + Debug {}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![deny(clippy::unwrap_used)]
|
||||
|
||||
pub mod atf;
|
||||
pub mod backend;
|
||||
pub mod bitmap;
|
||||
pub mod blend;
|
||||
|
|
|
@ -347,13 +347,21 @@ pub struct VertexBufferWrapper {
|
|||
pub data_32_per_vertex: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TextureWrapper {
|
||||
texture: wgpu::Texture,
|
||||
}
|
||||
|
||||
impl IndexBuffer for IndexBufferWrapper {}
|
||||
impl VertexBuffer for VertexBufferWrapper {}
|
||||
impl ruffle_render::backend::Texture for TextureWrapper {}
|
||||
impl ruffle_render::backend::Texture for TextureWrapper {
|
||||
fn width(&self) -> u32 {
|
||||
self.texture.width()
|
||||
}
|
||||
fn height(&self) -> u32 {
|
||||
self.texture.height()
|
||||
}
|
||||
}
|
||||
|
||||
// Context3D.setVertexBufferAt supports up to 8 vertex buffer attributes
|
||||
const MAX_VERTEX_ATTRIBUTES: usize = 8;
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
package {
|
||||
import com.adobe.utils.AGALMiniAssembler;
|
||||
|
||||
import flash.display.Sprite;
|
||||
import flash.display.Stage3D;
|
||||
import flash.display.StageAlign;
|
||||
import flash.display.StageScaleMode;
|
||||
import flash.display3D.Context3D;
|
||||
import flash.display3D.Context3DBlendFactor;
|
||||
import flash.display3D.Context3DCompareMode;
|
||||
import flash.display3D.Context3DProgramType;
|
||||
import flash.display3D.Context3DRenderMode;
|
||||
import flash.display3D.Context3DStencilAction;
|
||||
import flash.display3D.Context3DTriangleFace;
|
||||
import flash.display3D.Context3DVertexBufferFormat;
|
||||
import flash.display3D.Context3DTextureFilter;
|
||||
import flash.display3D.Context3DWrapMode;
|
||||
import flash.display3D.IndexBuffer3D;
|
||||
import flash.display3D.Program3D;
|
||||
import flash.display3D.VertexBuffer3D;
|
||||
import flash.events.Event;
|
||||
import flash.events.KeyboardEvent;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.geom.Rectangle;
|
||||
import flash.text.TextField;
|
||||
import flash.text.TextFormat;
|
||||
import flash.ui.Keyboard;
|
||||
import flash.utils.Timer;
|
||||
import flash.display.MovieClip;
|
||||
import flash.display.Stage;
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.Bitmap;
|
||||
import flash.utils.ByteArray;
|
||||
|
||||
// Based on example from https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display3D/Context3D.html#setStencilActions
|
||||
public class Test extends MovieClip {
|
||||
public const viewWidth:Number = 500;
|
||||
public const viewHeight:Number = 500;
|
||||
|
||||
private var stage3D:Stage3D;
|
||||
private var renderContext:Context3D;
|
||||
private var indexList:IndexBuffer3D;
|
||||
private var vertexes:VertexBuffer3D;
|
||||
|
||||
private const VERTEX_SHADER:String =
|
||||
"add op, va0, vc0 \n" + // copy position to output, adding offset
|
||||
"mov v0, va1"; // copy uv to varying variable v0
|
||||
|
||||
private const FRAGMENT_SHADER:String =
|
||||
"tex oc, v0, fs0 <2d,clamp,linear,mipnone>";
|
||||
|
||||
private var vertexAssembly:AGALMiniAssembler = new AGALMiniAssembler(false);
|
||||
private var fragmentAssembly:AGALMiniAssembler = new AGALMiniAssembler(false);
|
||||
private var programPair:Program3D;
|
||||
|
||||
[Embed(source = "ruffle_logo.png")]
|
||||
public var RUFFLE_LOGO: Class;
|
||||
|
||||
[Embed(source = "circle.atf", mimeType = "application/octet-stream")]
|
||||
public var CIRCLE_ATF: Class;
|
||||
|
||||
public function Test() {
|
||||
stage3D = this.stage.stage3Ds[0];
|
||||
|
||||
// Add event listener before requesting the context
|
||||
stage3D.addEventListener(Event.CONTEXT3D_CREATE, contextCreated);
|
||||
stage3D.requestContext3D(Context3DRenderMode.AUTO, "standard");
|
||||
|
||||
// Compile shaders
|
||||
vertexAssembly.assemble(Context3DProgramType.VERTEX, VERTEX_SHADER, 2);
|
||||
fragmentAssembly.assemble(Context3DProgramType.FRAGMENT, FRAGMENT_SHADER, 2);
|
||||
}
|
||||
|
||||
// Note, context3DCreate event can happen at any time, such as when the hardware resources are taken by another process
|
||||
private function contextCreated(event:Event):void {
|
||||
renderContext = Stage3D(event.target).context3D;
|
||||
|
||||
renderContext.enableErrorChecking = true; // Can slow rendering - only turn on when developing/testing
|
||||
renderContext.configureBackBuffer(viewWidth, viewHeight, 4, true);
|
||||
|
||||
// Create vertex index list for the triangles
|
||||
var triangles:Vector.<uint> = Vector.<uint>([0, 1, 2, 0, 2, 3]);
|
||||
indexList = renderContext.createIndexBuffer(triangles.length);
|
||||
indexList.uploadFromVector(triangles, 0, triangles.length);
|
||||
|
||||
// Create vertexes
|
||||
const dataPerVertex:int = 5;
|
||||
var vertexData:Vector.<Number> = Vector.<Number>(
|
||||
[
|
||||
// x, y, z u, v
|
||||
0, 0, 0, 0, 1,
|
||||
0.5, 0, 0, 1, 1,
|
||||
0.5, 0.5, 0, 1, 0,
|
||||
0, 0.5, 0, 0, 0
|
||||
]);
|
||||
vertexes = renderContext.createVertexBuffer(vertexData.length / dataPerVertex, dataPerVertex);
|
||||
vertexes.uploadFromVector(vertexData, 0, vertexData.length / dataPerVertex);
|
||||
|
||||
// Identify vertex data inputs for vertex program
|
||||
renderContext.setVertexBufferAt(0, vertexes, 0, Context3DVertexBufferFormat.FLOAT_3); // va0 is position
|
||||
renderContext.setVertexBufferAt(1, vertexes, 3, Context3DVertexBufferFormat.FLOAT_2); // va1 is texture uv coords
|
||||
|
||||
var logo: BitmapData = new RUFFLE_LOGO().bitmapData;
|
||||
|
||||
var bgraPixels = new ByteArray();
|
||||
// Test that we can skip over these first 4 bytse using byteArrayOffset
|
||||
// during uploading to the texture
|
||||
bgraPixels.writeInt(0xFFFFFFFF);
|
||||
var temp = logo.getPixels(logo.rect);
|
||||
bgraPixels.writeBytes(temp);
|
||||
|
||||
// Convert from big endian to little endian
|
||||
for (var i = 4; i < bgraPixels.length; i += 4) {
|
||||
var first = bgraPixels[i];
|
||||
var second = bgraPixels[i + 1];
|
||||
var third = bgraPixels[i + 2];
|
||||
var fourth = bgraPixels[i + 3];
|
||||
bgraPixels[i] = fourth;
|
||||
bgraPixels[i + 1] = third;
|
||||
bgraPixels[i + 2] = second;
|
||||
bgraPixels[i + 3] = first;
|
||||
}
|
||||
|
||||
renderContext.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);
|
||||
// Upload programs to render context
|
||||
programPair = renderContext.createProgram();
|
||||
programPair.upload(vertexAssembly.agalcode, fragmentAssembly.agalcode);
|
||||
renderContext.setProgram(programPair);
|
||||
|
||||
var bitmapDataTexture = renderContext.createRectangleTexture(logo.width, logo.height, "bgra", false);
|
||||
bitmapDataTexture.uploadFromBitmapData(logo);
|
||||
|
||||
var byteArrayTexture = renderContext.createRectangleTexture(logo.width, logo.height, "bgra", false);
|
||||
byteArrayTexture.uploadFromByteArray(bgraPixels, 4);
|
||||
|
||||
var circleATF: ByteArray = new CIRCLE_ATF();
|
||||
var atfTexture = renderContext.createTexture(512, 512, "bgra", false);
|
||||
atfTexture.uploadCompressedTextureFromByteArray(circleATF, 0);
|
||||
|
||||
|
||||
// Clear, setting stencil to 0
|
||||
renderContext.clear(.3, .3, .3, 1, 1, 0);
|
||||
|
||||
|
||||
renderContext.setTextureAt(0, bitmapDataTexture);
|
||||
renderContext.setProgramConstantsFromVector("vertex", 0, Vector.<Number>([-0.7, 0.5, 0.0, 0.0]));
|
||||
renderContext.drawTriangles(indexList, 0, 2);
|
||||
|
||||
renderContext.setTextureAt(0, byteArrayTexture);
|
||||
renderContext.setProgramConstantsFromVector("vertex", 0, Vector.<Number>([-0.7, -0.6, 0.0, 0.0]));
|
||||
renderContext.drawTriangles(indexList, 0, 2);
|
||||
|
||||
renderContext.setTextureAt(0, atfTexture);
|
||||
renderContext.setProgramConstantsFromVector("vertex", 0, Vector.<Number>([0.0, 0.5, 0.0, 0.0]));
|
||||
renderContext.drawTriangles(indexList, 0, 2);
|
||||
|
||||
|
||||
renderContext.present();
|
||||
|
||||
// this.addChild(new Bitmap(redGreen));
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -0,0 +1,805 @@
|
|||
/*
|
||||
Copyright (c) 2011, Adobe Systems Incorporated
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Adobe Systems Incorporated nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.adobe.utils
|
||||
{
|
||||
// ===========================================================================
|
||||
// Imports
|
||||
// ---------------------------------------------------------------------------
|
||||
import flash.display3D.*;
|
||||
import flash.utils.*;
|
||||
|
||||
// ===========================================================================
|
||||
// Class
|
||||
// ---------------------------------------------------------------------------
|
||||
public class AGALMiniAssembler
|
||||
{ // ======================================================================
|
||||
// Constants
|
||||
// ----------------------------------------------------------------------
|
||||
protected static const REGEXP_OUTER_SPACES:RegExp = /^\s+|\s+$/g;
|
||||
|
||||
// ======================================================================
|
||||
// Properties
|
||||
// ----------------------------------------------------------------------
|
||||
// AGAL bytes and error buffer
|
||||
private var _agalcode:ByteArray = null;
|
||||
private var _error:String = "";
|
||||
|
||||
private var debugEnabled:Boolean = false;
|
||||
|
||||
private static var initialized:Boolean = false;
|
||||
public var verbose:Boolean = false;
|
||||
|
||||
// ======================================================================
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------
|
||||
public function get error():String { return _error; }
|
||||
public function get agalcode():ByteArray { return _agalcode; }
|
||||
|
||||
// ======================================================================
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
public function AGALMiniAssembler( debugging:Boolean = false ):void
|
||||
{
|
||||
debugEnabled = debugging;
|
||||
if ( !initialized )
|
||||
init();
|
||||
}
|
||||
// ======================================================================
|
||||
// Methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public function assemble2( ctx3d : Context3D, version:uint, vertexsrc:String, fragmentsrc:String ) : Program3D
|
||||
{
|
||||
var agalvertex : ByteArray = assemble ( VERTEX, vertexsrc, version );
|
||||
var agalfragment : ByteArray = assemble ( FRAGMENT, fragmentsrc, version );
|
||||
var prog : Program3D = ctx3d.createProgram();
|
||||
prog.upload(agalvertex,agalfragment);
|
||||
return prog;
|
||||
}
|
||||
|
||||
public function assemble( mode:String, source:String, version:uint=1, ignorelimits:Boolean=false ):ByteArray
|
||||
{
|
||||
var start:uint = getTimer();
|
||||
|
||||
_agalcode = new ByteArray();
|
||||
_error = "";
|
||||
|
||||
var isFrag:Boolean = false;
|
||||
|
||||
if ( mode == FRAGMENT )
|
||||
isFrag = true;
|
||||
else if ( mode != VERTEX )
|
||||
_error = 'ERROR: mode needs to be "' + FRAGMENT + '" or "' + VERTEX + '" but is "' + mode + '".';
|
||||
|
||||
agalcode.endian = Endian.LITTLE_ENDIAN;
|
||||
agalcode.writeByte( 0xa0 ); // tag version
|
||||
agalcode.writeUnsignedInt( version ); // AGAL version, big endian, bit pattern will be 0x01000000
|
||||
agalcode.writeByte( 0xa1 ); // tag program id
|
||||
agalcode.writeByte( isFrag ? 1 : 0 ); // vertex or fragment
|
||||
|
||||
initregmap(version, ignorelimits);
|
||||
|
||||
var lines:Array = source.replace( /[\f\n\r\v]+/g, "\n" ).split( "\n" );
|
||||
var nest:int = 0;
|
||||
var nops:int = 0;
|
||||
var i:int;
|
||||
var lng:int = lines.length;
|
||||
|
||||
for ( i = 0; i < lng && _error == ""; i++ )
|
||||
{
|
||||
var line:String = new String( lines[i] );
|
||||
line = line.replace( REGEXP_OUTER_SPACES, "" );
|
||||
|
||||
// remove comments
|
||||
var startcomment:int = line.search( "//" );
|
||||
if ( startcomment != -1 )
|
||||
line = line.slice( 0, startcomment );
|
||||
|
||||
// grab options
|
||||
var optsi:int = line.search( /<.*>/g );
|
||||
var opts:Array;
|
||||
if ( optsi != -1 )
|
||||
{
|
||||
opts = line.slice( optsi ).match( /([\w\.\-\+]+)/gi );
|
||||
line = line.slice( 0, optsi );
|
||||
}
|
||||
|
||||
// find opcode
|
||||
var opCode:Array = line.match( /^\w{3}/ig );
|
||||
if ( !opCode )
|
||||
{
|
||||
if ( line.length >= 3 )
|
||||
trace( "warning: bad line "+i+": "+lines[i] );
|
||||
continue;
|
||||
}
|
||||
var opFound:OpCode = OPMAP[ opCode[0] ];
|
||||
|
||||
// if debug is enabled, output the opcodes
|
||||
if ( debugEnabled )
|
||||
trace( opFound );
|
||||
|
||||
if ( opFound == null )
|
||||
{
|
||||
if ( line.length >= 3 )
|
||||
trace( "warning: bad line "+i+": "+lines[i] );
|
||||
continue;
|
||||
}
|
||||
|
||||
line = line.slice( line.search( opFound.name ) + opFound.name.length );
|
||||
|
||||
if ( ( opFound.flags & OP_VERSION2 ) && version<2 )
|
||||
{
|
||||
_error = "error: opcode requires version 2.";
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ( opFound.flags & OP_VERT_ONLY ) && isFrag )
|
||||
{
|
||||
_error = "error: opcode is only allowed in vertex programs.";
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ( opFound.flags & OP_FRAG_ONLY ) && !isFrag )
|
||||
{
|
||||
_error = "error: opcode is only allowed in fragment programs.";
|
||||
break;
|
||||
}
|
||||
if ( verbose )
|
||||
trace( "emit opcode=" + opFound );
|
||||
|
||||
agalcode.writeUnsignedInt( opFound.emitCode );
|
||||
nops++;
|
||||
|
||||
if ( nops > MAX_OPCODES )
|
||||
{
|
||||
_error = "error: too many opcodes. maximum is "+MAX_OPCODES+".";
|
||||
break;
|
||||
}
|
||||
|
||||
// get operands, use regexp
|
||||
var regs:Array;
|
||||
|
||||
// will match both syntax
|
||||
regs = line.match( /vc\[([vof][acostdip]?)(\d*)?(\.[xyzw](\+\d{1,3})?)?\](\.[xyzw]{1,4})?|([vof][acostdip]?)(\d*)?(\.[xyzw]{1,4})?/gi );
|
||||
|
||||
if ( !regs || regs.length != opFound.numRegister )
|
||||
{
|
||||
_error = "error: wrong number of operands. found "+regs.length+" but expected "+opFound.numRegister+".";
|
||||
break;
|
||||
}
|
||||
|
||||
var badreg:Boolean = false;
|
||||
var pad:uint = 64 + 64 + 32;
|
||||
var regLength:uint = regs.length;
|
||||
|
||||
for ( var j:int = 0; j < regLength; j++ )
|
||||
{
|
||||
var isRelative:Boolean = false;
|
||||
var relreg:Array = regs[ j ].match( /\[.*\]/ig );
|
||||
if ( relreg && relreg.length > 0 )
|
||||
{
|
||||
regs[ j ] = regs[ j ].replace( relreg[ 0 ], "0" );
|
||||
|
||||
if ( verbose )
|
||||
trace( "IS REL" );
|
||||
isRelative = true;
|
||||
}
|
||||
|
||||
var res:Array = regs[j].match( /^\b[A-Za-z]{1,2}/ig );
|
||||
if ( !res )
|
||||
{
|
||||
_error = "error: could not parse operand "+j+" ("+regs[j]+").";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
var regFound:Register = REGMAP[ res[ 0 ] ];
|
||||
|
||||
// if debug is enabled, output the registers
|
||||
if ( debugEnabled )
|
||||
trace( regFound );
|
||||
|
||||
if ( regFound == null )
|
||||
{
|
||||
_error = "error: could not find register name for operand "+j+" ("+regs[j]+").";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( isFrag )
|
||||
{
|
||||
if ( !( regFound.flags & REG_FRAG ) )
|
||||
{
|
||||
_error = "error: register operand "+j+" ("+regs[j]+") only allowed in vertex programs.";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
if ( isRelative )
|
||||
{
|
||||
_error = "error: register operand "+j+" ("+regs[j]+") relative adressing not allowed in fragment programs.";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !( regFound.flags & REG_VERT ) )
|
||||
{
|
||||
_error = "error: register operand "+j+" ("+regs[j]+") only allowed in fragment programs.";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
regs[j] = regs[j].slice( regs[j].search( regFound.name ) + regFound.name.length );
|
||||
//trace( "REGNUM: " +regs[j] );
|
||||
var idxmatch:Array = isRelative ? relreg[0].match( /\d+/ ) : regs[j].match( /\d+/ );
|
||||
var regidx:uint = 0;
|
||||
|
||||
if ( idxmatch )
|
||||
regidx = uint( idxmatch[0] );
|
||||
|
||||
if ( regFound.range < regidx )
|
||||
{
|
||||
_error = "error: register operand "+j+" ("+regs[j]+") index exceeds limit of "+(regFound.range+1)+".";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
var regmask:uint = 0;
|
||||
var maskmatch:Array = regs[j].match( /(\.[xyzw]{1,4})/ );
|
||||
var isDest:Boolean = ( j == 0 && !( opFound.flags & OP_NO_DEST ) );
|
||||
var isSampler:Boolean = ( j == 2 && ( opFound.flags & OP_SPECIAL_TEX ) );
|
||||
var reltype:uint = 0;
|
||||
var relsel:uint = 0;
|
||||
var reloffset:int = 0;
|
||||
|
||||
if ( isDest && isRelative )
|
||||
{
|
||||
_error = "error: relative can not be destination";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( maskmatch )
|
||||
{
|
||||
regmask = 0;
|
||||
var cv:uint;
|
||||
var maskLength:uint = maskmatch[0].length;
|
||||
for ( var k:int = 1; k < maskLength; k++ )
|
||||
{
|
||||
cv = maskmatch[0].charCodeAt(k) - "x".charCodeAt(0);
|
||||
if ( cv > 2 )
|
||||
cv = 3;
|
||||
if ( isDest )
|
||||
regmask |= 1 << cv;
|
||||
else
|
||||
regmask |= cv << ( ( k - 1 ) << 1 );
|
||||
}
|
||||
if ( !isDest )
|
||||
for ( ; k <= 4; k++ )
|
||||
regmask |= cv << ( ( k - 1 ) << 1 ); // repeat last
|
||||
}
|
||||
else
|
||||
{
|
||||
regmask = isDest ? 0xf : 0xe4; // id swizzle or mask
|
||||
}
|
||||
|
||||
if ( isRelative )
|
||||
{
|
||||
var relname:Array = relreg[0].match( /[A-Za-z]{1,2}/ig );
|
||||
var regFoundRel:Register = REGMAP[ relname[0]];
|
||||
if ( regFoundRel == null )
|
||||
{
|
||||
_error = "error: bad index register";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
reltype = regFoundRel.emitCode;
|
||||
var selmatch:Array = relreg[0].match( /(\.[xyzw]{1,1})/ );
|
||||
if ( selmatch.length==0 )
|
||||
{
|
||||
_error = "error: bad index register select";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
relsel = selmatch[0].charCodeAt(1) - "x".charCodeAt(0);
|
||||
if ( relsel > 2 )
|
||||
relsel = 3;
|
||||
var relofs:Array = relreg[0].match( /\+\d{1,3}/ig );
|
||||
if ( relofs.length > 0 )
|
||||
reloffset = relofs[0];
|
||||
if ( reloffset < 0 || reloffset > 255 )
|
||||
{
|
||||
_error = "error: index offset "+reloffset+" out of bounds. [0..255]";
|
||||
badreg = true;
|
||||
break;
|
||||
}
|
||||
if ( verbose )
|
||||
trace( "RELATIVE: type="+reltype+"=="+relname[0]+" sel="+relsel+"=="+selmatch[0]+" idx="+regidx+" offset="+reloffset );
|
||||
}
|
||||
|
||||
if ( verbose )
|
||||
trace( " emit argcode="+regFound+"["+regidx+"]["+regmask+"]" );
|
||||
if ( isDest )
|
||||
{
|
||||
agalcode.writeShort( regidx );
|
||||
agalcode.writeByte( regmask );
|
||||
agalcode.writeByte( regFound.emitCode );
|
||||
pad -= 32;
|
||||
} else
|
||||
{
|
||||
if ( isSampler )
|
||||
{
|
||||
if ( verbose )
|
||||
trace( " emit sampler" );
|
||||
var samplerbits:uint = 5; // type 5
|
||||
var optsLength:uint = opts == null ? 0 : opts.length;
|
||||
var bias:Number = 0;
|
||||
for ( k = 0; k<optsLength; k++ )
|
||||
{
|
||||
if ( verbose )
|
||||
trace( " opt: "+opts[k] );
|
||||
var optfound:Sampler = SAMPLEMAP [opts[k]];
|
||||
if ( optfound == null )
|
||||
{
|
||||
// todo check that it's a number...
|
||||
//trace( "Warning, unknown sampler option: "+opts[k] );
|
||||
bias = Number(opts[k]);
|
||||
if ( verbose )
|
||||
trace( " bias: " + bias );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( optfound.flag != SAMPLER_SPECIAL_SHIFT )
|
||||
samplerbits &= ~( 0xf << optfound.flag );
|
||||
samplerbits |= uint( optfound.mask ) << uint( optfound.flag );
|
||||
}
|
||||
}
|
||||
agalcode.writeShort( regidx );
|
||||
agalcode.writeByte(int(bias*8.0));
|
||||
agalcode.writeByte(0);
|
||||
agalcode.writeUnsignedInt( samplerbits );
|
||||
|
||||
if ( verbose )
|
||||
trace( " bits: " + ( samplerbits - 5 ) );
|
||||
pad -= 64;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( j == 0 )
|
||||
{
|
||||
agalcode.writeUnsignedInt( 0 );
|
||||
pad -= 32;
|
||||
}
|
||||
agalcode.writeShort( regidx );
|
||||
agalcode.writeByte( reloffset );
|
||||
agalcode.writeByte( regmask );
|
||||
agalcode.writeByte( regFound.emitCode );
|
||||
agalcode.writeByte( reltype );
|
||||
agalcode.writeShort( isRelative ? ( relsel | ( 1 << 15 ) ) : 0 );
|
||||
|
||||
pad -= 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pad unused regs
|
||||
for ( j = 0; j < pad; j += 8 )
|
||||
agalcode.writeByte( 0 );
|
||||
|
||||
if ( badreg )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( _error != "" )
|
||||
{
|
||||
_error += "\n at line " + i + " " + lines[i];
|
||||
agalcode.length = 0;
|
||||
trace( _error );
|
||||
}
|
||||
|
||||
// trace the bytecode bytes if debugging is enabled
|
||||
if ( debugEnabled )
|
||||
{
|
||||
var dbgLine:String = "generated bytecode:";
|
||||
var agalLength:uint = agalcode.length;
|
||||
for ( var index:uint = 0; index < agalLength; index++ )
|
||||
{
|
||||
if ( !( index % 16 ) )
|
||||
dbgLine += "\n";
|
||||
if ( !( index % 4 ) )
|
||||
dbgLine += " ";
|
||||
|
||||
var byteStr:String = agalcode[ index ].toString( 16 );
|
||||
if ( byteStr.length < 2 )
|
||||
byteStr = "0" + byteStr;
|
||||
|
||||
dbgLine += byteStr;
|
||||
}
|
||||
trace( dbgLine );
|
||||
}
|
||||
|
||||
if ( verbose )
|
||||
trace( "AGALMiniAssembler.assemble time: " + ( ( getTimer() - start ) / 1000 ) + "s" );
|
||||
|
||||
return agalcode;
|
||||
}
|
||||
|
||||
private function initregmap ( version:uint, ignorelimits:Boolean ) : void {
|
||||
// version changes limits
|
||||
REGMAP[ VA ] = new Register( VA, "vertex attribute", 0x0, ignorelimits?1024:7, REG_VERT | REG_READ );
|
||||
REGMAP[ VC ] = new Register( VC, "vertex constant", 0x1, ignorelimits?1024:(version==1?127:249), REG_VERT | REG_READ );
|
||||
REGMAP[ VT ] = new Register( VT, "vertex temporary", 0x2, ignorelimits?1024:(version==1?7:25), REG_VERT | REG_WRITE | REG_READ );
|
||||
REGMAP[ VO ] = new Register( VO, "vertex output", 0x3, ignorelimits?1024:0, REG_VERT | REG_WRITE );
|
||||
REGMAP[ VI ] = new Register( VI, "varying", 0x4, ignorelimits?1024:(version==1?7:9), REG_VERT | REG_FRAG | REG_READ | REG_WRITE );
|
||||
REGMAP[ FC ] = new Register( FC, "fragment constant", 0x1, ignorelimits?1024:(version==1?27:63), REG_FRAG | REG_READ );
|
||||
REGMAP[ FT ] = new Register( FT, "fragment temporary", 0x2, ignorelimits?1024:(version==1?7:25), REG_FRAG | REG_WRITE | REG_READ );
|
||||
REGMAP[ FS ] = new Register( FS, "texture sampler", 0x5, ignorelimits?1024:7, REG_FRAG | REG_READ );
|
||||
REGMAP[ FO ] = new Register( FO, "fragment output", 0x3, ignorelimits?1024:(version==1?0:3), REG_FRAG | REG_WRITE );
|
||||
REGMAP[ FD ] = new Register( FD, "fragment depth output",0x6, ignorelimits?1024:(version==1?-1:0), REG_FRAG | REG_WRITE );
|
||||
|
||||
// aliases
|
||||
REGMAP[ "op" ] = REGMAP[ VO ];
|
||||
REGMAP[ "i" ] = REGMAP[ VI ];
|
||||
REGMAP[ "v" ] = REGMAP[ VI ];
|
||||
REGMAP[ "oc" ] = REGMAP[ FO ];
|
||||
REGMAP[ "od" ] = REGMAP[ FD ];
|
||||
REGMAP[ "fi" ] = REGMAP[ VI ];
|
||||
}
|
||||
|
||||
static private function init():void
|
||||
{
|
||||
initialized = true;
|
||||
|
||||
// Fill the dictionaries with opcodes and registers
|
||||
OPMAP[ MOV ] = new OpCode( MOV, 2, 0x00, 0 );
|
||||
OPMAP[ ADD ] = new OpCode( ADD, 3, 0x01, 0 );
|
||||
OPMAP[ SUB ] = new OpCode( SUB, 3, 0x02, 0 );
|
||||
OPMAP[ MUL ] = new OpCode( MUL, 3, 0x03, 0 );
|
||||
OPMAP[ DIV ] = new OpCode( DIV, 3, 0x04, 0 );
|
||||
OPMAP[ RCP ] = new OpCode( RCP, 2, 0x05, 0 );
|
||||
OPMAP[ MIN ] = new OpCode( MIN, 3, 0x06, 0 );
|
||||
OPMAP[ MAX ] = new OpCode( MAX, 3, 0x07, 0 );
|
||||
OPMAP[ FRC ] = new OpCode( FRC, 2, 0x08, 0 );
|
||||
OPMAP[ SQT ] = new OpCode( SQT, 2, 0x09, 0 );
|
||||
OPMAP[ RSQ ] = new OpCode( RSQ, 2, 0x0a, 0 );
|
||||
OPMAP[ POW ] = new OpCode( POW, 3, 0x0b, 0 );
|
||||
OPMAP[ LOG ] = new OpCode( LOG, 2, 0x0c, 0 );
|
||||
OPMAP[ EXP ] = new OpCode( EXP, 2, 0x0d, 0 );
|
||||
OPMAP[ NRM ] = new OpCode( NRM, 2, 0x0e, 0 );
|
||||
OPMAP[ SIN ] = new OpCode( SIN, 2, 0x0f, 0 );
|
||||
OPMAP[ COS ] = new OpCode( COS, 2, 0x10, 0 );
|
||||
OPMAP[ CRS ] = new OpCode( CRS, 3, 0x11, 0 );
|
||||
OPMAP[ DP3 ] = new OpCode( DP3, 3, 0x12, 0 );
|
||||
OPMAP[ DP4 ] = new OpCode( DP4, 3, 0x13, 0 );
|
||||
OPMAP[ ABS ] = new OpCode( ABS, 2, 0x14, 0 );
|
||||
OPMAP[ NEG ] = new OpCode( NEG, 2, 0x15, 0 );
|
||||
OPMAP[ SAT ] = new OpCode( SAT, 2, 0x16, 0 );
|
||||
OPMAP[ M33 ] = new OpCode( M33, 3, 0x17, OP_SPECIAL_MATRIX );
|
||||
OPMAP[ M44 ] = new OpCode( M44, 3, 0x18, OP_SPECIAL_MATRIX );
|
||||
OPMAP[ M34 ] = new OpCode( M34, 3, 0x19, OP_SPECIAL_MATRIX );
|
||||
OPMAP[ DDX ] = new OpCode( DDX, 2, 0x1a, OP_VERSION2 | OP_FRAG_ONLY );
|
||||
OPMAP[ DDY ] = new OpCode( DDY, 2, 0x1b, OP_VERSION2 | OP_FRAG_ONLY );
|
||||
OPMAP[ IFE ] = new OpCode( IFE, 2, 0x1c, OP_NO_DEST | OP_VERSION2 | OP_INCNEST | OP_SCALAR );
|
||||
OPMAP[ INE ] = new OpCode( INE, 2, 0x1d, OP_NO_DEST | OP_VERSION2 | OP_INCNEST | OP_SCALAR );
|
||||
OPMAP[ IFG ] = new OpCode( IFG, 2, 0x1e, OP_NO_DEST | OP_VERSION2 | OP_INCNEST | OP_SCALAR );
|
||||
OPMAP[ IFL ] = new OpCode( IFL, 2, 0x1f, OP_NO_DEST | OP_VERSION2 | OP_INCNEST | OP_SCALAR );
|
||||
OPMAP[ ELS ] = new OpCode( ELS, 0, 0x20, OP_NO_DEST | OP_VERSION2 | OP_INCNEST | OP_DECNEST | OP_SCALAR );
|
||||
OPMAP[ EIF ] = new OpCode( EIF, 0, 0x21, OP_NO_DEST | OP_VERSION2 | OP_DECNEST | OP_SCALAR );
|
||||
// space
|
||||
//OPMAP[ TED ] = new OpCode( TED, 3, 0x26, OP_FRAG_ONLY | OP_SPECIAL_TEX | OP_VERSION2); //ted is not available in AGAL2
|
||||
OPMAP[ KIL ] = new OpCode( KIL, 1, 0x27, OP_NO_DEST | OP_FRAG_ONLY );
|
||||
OPMAP[ TEX ] = new OpCode( TEX, 3, 0x28, OP_FRAG_ONLY | OP_SPECIAL_TEX );
|
||||
OPMAP[ SGE ] = new OpCode( SGE, 3, 0x29, 0 );
|
||||
OPMAP[ SLT ] = new OpCode( SLT, 3, 0x2a, 0 );
|
||||
OPMAP[ SGN ] = new OpCode( SGN, 2, 0x2b, 0 );
|
||||
OPMAP[ SEQ ] = new OpCode( SEQ, 3, 0x2c, 0 );
|
||||
OPMAP[ SNE ] = new OpCode( SNE, 3, 0x2d, 0 );
|
||||
|
||||
|
||||
SAMPLEMAP[ RGBA ] = new Sampler( RGBA, SAMPLER_TYPE_SHIFT, 0 );
|
||||
SAMPLEMAP[ DXT1 ] = new Sampler( DXT1, SAMPLER_TYPE_SHIFT, 1 );
|
||||
SAMPLEMAP[ DXT5 ] = new Sampler( DXT5, SAMPLER_TYPE_SHIFT, 2 );
|
||||
SAMPLEMAP[ VIDEO ] = new Sampler( VIDEO, SAMPLER_TYPE_SHIFT, 3 );
|
||||
SAMPLEMAP[ D2 ] = new Sampler( D2, SAMPLER_DIM_SHIFT, 0 );
|
||||
SAMPLEMAP[ D3 ] = new Sampler( D3, SAMPLER_DIM_SHIFT, 2 );
|
||||
SAMPLEMAP[ CUBE ] = new Sampler( CUBE, SAMPLER_DIM_SHIFT, 1 );
|
||||
SAMPLEMAP[ MIPNEAREST ] = new Sampler( MIPNEAREST, SAMPLER_MIPMAP_SHIFT, 1 );
|
||||
SAMPLEMAP[ MIPLINEAR ] = new Sampler( MIPLINEAR, SAMPLER_MIPMAP_SHIFT, 2 );
|
||||
SAMPLEMAP[ MIPNONE ] = new Sampler( MIPNONE, SAMPLER_MIPMAP_SHIFT, 0 );
|
||||
SAMPLEMAP[ NOMIP ] = new Sampler( NOMIP, SAMPLER_MIPMAP_SHIFT, 0 );
|
||||
SAMPLEMAP[ NEAREST ] = new Sampler( NEAREST, SAMPLER_FILTER_SHIFT, 0 );
|
||||
SAMPLEMAP[ LINEAR ] = new Sampler( LINEAR, SAMPLER_FILTER_SHIFT, 1 );
|
||||
SAMPLEMAP[ ANISOTROPIC2X ] = new Sampler( ANISOTROPIC2X, SAMPLER_FILTER_SHIFT, 2 );
|
||||
SAMPLEMAP[ ANISOTROPIC4X ] = new Sampler( ANISOTROPIC4X, SAMPLER_FILTER_SHIFT, 3 );
|
||||
SAMPLEMAP[ ANISOTROPIC8X ] = new Sampler( ANISOTROPIC8X, SAMPLER_FILTER_SHIFT, 4 );
|
||||
SAMPLEMAP[ ANISOTROPIC16X ] = new Sampler( ANISOTROPIC16X, SAMPLER_FILTER_SHIFT,5 );
|
||||
SAMPLEMAP[ CENTROID ] = new Sampler( CENTROID, SAMPLER_SPECIAL_SHIFT, 1 << 0 );
|
||||
SAMPLEMAP[ SINGLE ] = new Sampler( SINGLE, SAMPLER_SPECIAL_SHIFT, 1 << 1 );
|
||||
SAMPLEMAP[ IGNORESAMPLER ] = new Sampler( IGNORESAMPLER, SAMPLER_SPECIAL_SHIFT, 1 << 2 );
|
||||
SAMPLEMAP[ REPEAT ] = new Sampler( REPEAT, SAMPLER_REPEAT_SHIFT, 1 );
|
||||
SAMPLEMAP[ WRAP ] = new Sampler( WRAP, SAMPLER_REPEAT_SHIFT, 1 );
|
||||
SAMPLEMAP[ CLAMP ] = new Sampler( CLAMP, SAMPLER_REPEAT_SHIFT, 0 );
|
||||
SAMPLEMAP[ CLAMP_U_REPEAT_V ] = new Sampler( CLAMP_U_REPEAT_V, SAMPLER_REPEAT_SHIFT, 2 );
|
||||
SAMPLEMAP[ REPEAT_U_CLAMP_V ] = new Sampler( REPEAT_U_CLAMP_V, SAMPLER_REPEAT_SHIFT, 3 );
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Constants
|
||||
// ----------------------------------------------------------------------
|
||||
private static const OPMAP:Dictionary = new Dictionary();
|
||||
private static const REGMAP:Dictionary = new Dictionary();
|
||||
private static const SAMPLEMAP:Dictionary = new Dictionary();
|
||||
|
||||
private static const MAX_NESTING:int = 4;
|
||||
private static const MAX_OPCODES:int = 2048;
|
||||
|
||||
private static const FRAGMENT:String = "fragment";
|
||||
private static const VERTEX:String = "vertex";
|
||||
|
||||
// masks and shifts
|
||||
private static const SAMPLER_TYPE_SHIFT:uint = 8;
|
||||
private static const SAMPLER_DIM_SHIFT:uint = 12;
|
||||
private static const SAMPLER_SPECIAL_SHIFT:uint = 16;
|
||||
private static const SAMPLER_REPEAT_SHIFT:uint = 20;
|
||||
private static const SAMPLER_MIPMAP_SHIFT:uint = 24;
|
||||
private static const SAMPLER_FILTER_SHIFT:uint = 28;
|
||||
|
||||
// regmap flags
|
||||
private static const REG_WRITE:uint = 0x1;
|
||||
private static const REG_READ:uint = 0x2;
|
||||
private static const REG_FRAG:uint = 0x20;
|
||||
private static const REG_VERT:uint = 0x40;
|
||||
|
||||
// opmap flags
|
||||
private static const OP_SCALAR:uint = 0x1;
|
||||
private static const OP_SPECIAL_TEX:uint = 0x8;
|
||||
private static const OP_SPECIAL_MATRIX:uint = 0x10;
|
||||
private static const OP_FRAG_ONLY:uint = 0x20;
|
||||
private static const OP_VERT_ONLY:uint = 0x40;
|
||||
private static const OP_NO_DEST:uint = 0x80;
|
||||
private static const OP_VERSION2:uint = 0x100;
|
||||
private static const OP_INCNEST:uint = 0x200;
|
||||
private static const OP_DECNEST:uint = 0x400;
|
||||
|
||||
// opcodes
|
||||
private static const MOV:String = "mov";
|
||||
private static const ADD:String = "add";
|
||||
private static const SUB:String = "sub";
|
||||
private static const MUL:String = "mul";
|
||||
private static const DIV:String = "div";
|
||||
private static const RCP:String = "rcp";
|
||||
private static const MIN:String = "min";
|
||||
private static const MAX:String = "max";
|
||||
private static const FRC:String = "frc";
|
||||
private static const SQT:String = "sqt";
|
||||
private static const RSQ:String = "rsq";
|
||||
private static const POW:String = "pow";
|
||||
private static const LOG:String = "log";
|
||||
private static const EXP:String = "exp";
|
||||
private static const NRM:String = "nrm";
|
||||
private static const SIN:String = "sin";
|
||||
private static const COS:String = "cos";
|
||||
private static const CRS:String = "crs";
|
||||
private static const DP3:String = "dp3";
|
||||
private static const DP4:String = "dp4";
|
||||
private static const ABS:String = "abs";
|
||||
private static const NEG:String = "neg";
|
||||
private static const SAT:String = "sat";
|
||||
private static const M33:String = "m33";
|
||||
private static const M44:String = "m44";
|
||||
private static const M34:String = "m34";
|
||||
private static const DDX:String = "ddx";
|
||||
private static const DDY:String = "ddy";
|
||||
private static const IFE:String = "ife";
|
||||
private static const INE:String = "ine";
|
||||
private static const IFG:String = "ifg";
|
||||
private static const IFL:String = "ifl";
|
||||
private static const ELS:String = "els";
|
||||
private static const EIF:String = "eif";
|
||||
private static const TED:String = "ted";
|
||||
private static const KIL:String = "kil";
|
||||
private static const TEX:String = "tex";
|
||||
private static const SGE:String = "sge";
|
||||
private static const SLT:String = "slt";
|
||||
private static const SGN:String = "sgn";
|
||||
private static const SEQ:String = "seq";
|
||||
private static const SNE:String = "sne";
|
||||
|
||||
// registers
|
||||
private static const VA:String = "va";
|
||||
private static const VC:String = "vc";
|
||||
private static const VT:String = "vt";
|
||||
private static const VO:String = "vo";
|
||||
private static const VI:String = "vi";
|
||||
private static const FC:String = "fc";
|
||||
private static const FT:String = "ft";
|
||||
private static const FS:String = "fs";
|
||||
private static const FO:String = "fo";
|
||||
private static const FD:String = "fd";
|
||||
|
||||
// samplers
|
||||
private static const D2:String = "2d";
|
||||
private static const D3:String = "3d";
|
||||
private static const CUBE:String = "cube";
|
||||
private static const MIPNEAREST:String = "mipnearest";
|
||||
private static const MIPLINEAR:String = "miplinear";
|
||||
private static const MIPNONE:String = "mipnone";
|
||||
private static const NOMIP:String = "nomip";
|
||||
private static const NEAREST:String = "nearest";
|
||||
private static const LINEAR:String = "linear";
|
||||
private static const ANISOTROPIC2X:String = "anisotropic2x"; //Introduced by Flash 14
|
||||
private static const ANISOTROPIC4X:String = "anisotropic4x"; //Introduced by Flash 14
|
||||
private static const ANISOTROPIC8X:String = "anisotropic8x"; //Introduced by Flash 14
|
||||
private static const ANISOTROPIC16X:String = "anisotropic16x"; //Introduced by Flash 14
|
||||
private static const CENTROID:String = "centroid";
|
||||
private static const SINGLE:String = "single";
|
||||
private static const IGNORESAMPLER:String = "ignoresampler";
|
||||
private static const REPEAT:String = "repeat";
|
||||
private static const WRAP:String = "wrap";
|
||||
private static const CLAMP:String = "clamp";
|
||||
private static const REPEAT_U_CLAMP_V:String = "repeat_u_clamp_v"; //Introduced by Flash 13
|
||||
private static const CLAMP_U_REPEAT_V:String = "clamp_u_repeat_v"; //Introduced by Flash 13
|
||||
private static const RGBA:String = "rgba";
|
||||
private static const DXT1:String = "dxt1";
|
||||
private static const DXT5:String = "dxt5";
|
||||
private static const VIDEO:String = "video";
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
// Helper Classes
|
||||
// --------------------------------------------------------------------------------
|
||||
{
|
||||
// ===========================================================================
|
||||
// Class
|
||||
// ---------------------------------------------------------------------------
|
||||
class OpCode
|
||||
{
|
||||
// ======================================================================
|
||||
// Properties
|
||||
// ----------------------------------------------------------------------
|
||||
private var _emitCode:uint;
|
||||
private var _flags:uint;
|
||||
private var _name:String;
|
||||
private var _numRegister:uint;
|
||||
|
||||
// ======================================================================
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------
|
||||
public function get emitCode():uint { return _emitCode; }
|
||||
public function get flags():uint { return _flags; }
|
||||
public function get name():String { return _name; }
|
||||
public function get numRegister():uint { return _numRegister; }
|
||||
|
||||
// ======================================================================
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
public function OpCode( name:String, numRegister:uint, emitCode:uint, flags:uint)
|
||||
{
|
||||
_name = name;
|
||||
_numRegister = numRegister;
|
||||
_emitCode = emitCode;
|
||||
_flags = flags;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Methods
|
||||
// ----------------------------------------------------------------------
|
||||
public function toString():String
|
||||
{
|
||||
return "[OpCode name=\""+_name+"\", numRegister="+_numRegister+", emitCode="+_emitCode+", flags="+_flags+"]";
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Class
|
||||
// ---------------------------------------------------------------------------
|
||||
class Register
|
||||
{
|
||||
// ======================================================================
|
||||
// Properties
|
||||
// ----------------------------------------------------------------------
|
||||
private var _emitCode:uint;
|
||||
private var _name:String;
|
||||
private var _longName:String;
|
||||
private var _flags:uint;
|
||||
private var _range:uint;
|
||||
|
||||
// ======================================================================
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------
|
||||
public function get emitCode():uint { return _emitCode; }
|
||||
public function get longName():String { return _longName; }
|
||||
public function get name():String { return _name; }
|
||||
public function get flags():uint { return _flags; }
|
||||
public function get range():uint { return _range; }
|
||||
|
||||
// ======================================================================
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
public function Register( name:String, longName:String, emitCode:uint, range:uint, flags:uint)
|
||||
{
|
||||
_name = name;
|
||||
_longName = longName;
|
||||
_emitCode = emitCode;
|
||||
_range = range;
|
||||
_flags = flags;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Methods
|
||||
// ----------------------------------------------------------------------
|
||||
public function toString():String
|
||||
{
|
||||
return "[Register name=\""+_name+"\", longName=\""+_longName+"\", emitCode="+_emitCode+", range="+_range+", flags="+ _flags+"]";
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Class
|
||||
// ---------------------------------------------------------------------------
|
||||
class Sampler
|
||||
{
|
||||
// ======================================================================
|
||||
// Properties
|
||||
// ----------------------------------------------------------------------
|
||||
private var _flag:uint;
|
||||
private var _mask:uint;
|
||||
private var _name:String;
|
||||
|
||||
// ======================================================================
|
||||
// Getters
|
||||
// ----------------------------------------------------------------------
|
||||
public function get flag():uint { return _flag; }
|
||||
public function get mask():uint { return _mask; }
|
||||
public function get name():String { return _name; }
|
||||
|
||||
// ======================================================================
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
public function Sampler( name:String, flag:uint, mask:uint )
|
||||
{
|
||||
_name = name;
|
||||
_flag = flag;
|
||||
_mask = mask;
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// Methods
|
||||
// ----------------------------------------------------------------------
|
||||
public function toString():String
|
||||
{
|
||||
return "[Sampler name=\""+_name+"\", flag=\""+_flag+"\", mask="+mask+"]";
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
num_frames = 1
|
||||
|
||||
[player_options]
|
||||
with_renderer = { optional = true, sample_count = 1 }
|
Loading…
Reference in New Issue