Merge pull request #4 from lioncash/android-about-menu-info

Expand the Android app's about menu to include additional info about a user's device.
This commit is contained in:
neobrain 2014-01-28 15:19:05 -08:00
commit 7b459d2463
18 changed files with 1544 additions and 320 deletions

View File

@ -33,6 +33,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="org.dolphinemu.dolphinemu.about.AboutActivity"/>
<activity android:name="org.dolphinemu.dolphinemu.gamelist.GameListActivity"/> <activity android:name="org.dolphinemu.dolphinemu.gamelist.GameListActivity"/>
<activity <activity

View File

@ -12,6 +12,21 @@
<string name="build_revision">ビルドのバージョン</string> <string name="build_revision">ビルドのバージョン</string>
<string name="supports_gles3">サポートのOpenGL ES 3</string> <string name="supports_gles3">サポートのOpenGL ES 3</string>
<string name="supports_neon">サポートのNEON</string> <string name="supports_neon">サポートのNEON</string>
<string name="cpu">CPU</string>
<string name="gles_two">GLES 2</string>
<string name="gles_three">GLES 3</string>
<string name="desktop_gl">OpenGL</string>
<!-- About Menu - CPU Info -->
<string name="cpu_abi_one">CPU ABI 1</string>
<string name="cpu_abi_two">CPU ABI 2</string>
<string name="cpu_info">CPU情報</string>
<string name="cpu_type">CPUタイプ</string>
<string name="cpu_features">CPUの命令セットと機能</string>
<string name="num_cores">CPUコア</string>
<string name="cpu_implementer">CPUの実装</string>
<string name="cpu_hardware">ハードウェア</string>
<string name="unknown_part_num">不明 (%1$d)。 我々はそれを文書化することができますので、この番号を報告してください。 </string>
<!-- Folder Browser --> <!-- Folder Browser -->
<string name="current_dir">現在のディレクトリ: %1$s</string> <string name="current_dir">現在のディレクトリ: %1$s</string>

View File

@ -12,6 +12,22 @@
<string name="build_revision">Build Revision</string> <string name="build_revision">Build Revision</string>
<string name="supports_gles3">Supports OpenGL ES 3</string> <string name="supports_gles3">Supports OpenGL ES 3</string>
<string name="supports_neon">Supports NEON</string> <string name="supports_neon">Supports NEON</string>
<string name="general">General</string>
<string name="cpu">CPU</string>
<string name="gles_two">GLES 2</string>
<string name="gles_three">GLES 3</string>
<string name="desktop_gl">OpenGL</string>
<!-- About Menu - CPU Info -->
<string name="cpu_abi_one">CPU ABI 1</string>
<string name="cpu_abi_two">CPU ABI 2</string>
<string name="cpu_info">CPU Info</string>
<string name="cpu_type">CPU Type</string>
<string name="cpu_features">CPU Features</string>
<string name="num_cores">Number of Cores</string>
<string name="cpu_implementer">CPU Implementer</string>
<string name="cpu_hardware">Hardware</string>
<string name="unknown_part_num">Unknown (%1$d). Please report this number so it can be documented!</string>
<!-- Folder Browser --> <!-- Folder Browser -->
<string name="current_dir">Current Dir: %1$s</string> <string name="current_dir">Current Dir: %1$s</string>

View File

@ -1,119 +0,0 @@
/**
* Copyright 2013 Dolphin Emulator Project
* Licensed under GPLv2
* Refer to the license.txt file included.
*/
package org.dolphinemu.dolphinemu;
import android.app.ListFragment;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import org.dolphinemu.dolphinemu.settings.video.VideoSettingsFragment;
/**
* Represents the about screen.
*/
public final class AboutFragment extends ListFragment
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ListView rootView = (ListView) inflater.inflate(R.layout.gamelist_listview, container, false);
final String yes = getString(R.string.yes);
final String no = getString(R.string.no);
List<AboutFragmentItem> Input = new ArrayList<AboutFragmentItem>();
Input.add(new AboutFragmentItem(getString(R.string.build_revision), NativeLibrary.GetVersionString()));
Input.add(new AboutFragmentItem(getString(R.string.supports_gles3), VideoSettingsFragment.SupportsGLES3() ? yes : no));
Input.add(new AboutFragmentItem(getString(R.string.supports_neon), NativeLibrary.SupportsNEON() ? yes : no));
AboutFragmentAdapter adapter = new AboutFragmentAdapter(getActivity(), R.layout.about_layout, Input);
rootView.setAdapter(adapter);
rootView.setEnabled(false); // Makes the list view non-clickable.
return rootView;
}
// Represents an item in the AboutFragment.
private static final class AboutFragmentItem
{
private final String title;
private final String subtitle;
public AboutFragmentItem(String title, String subtitle)
{
this.title = title;
this.subtitle = subtitle;
}
public String getTitle()
{
return title;
}
public String getSubTitle()
{
return subtitle;
}
}
// The adapter that manages the displaying of items in this AboutFragment.
private static final class AboutFragmentAdapter extends ArrayAdapter<AboutFragmentItem>
{
private final Context ctx;
private final int id;
private final List<AboutFragmentItem> items;
public AboutFragmentAdapter(Context ctx, int id, List<AboutFragmentItem> items)
{
super(ctx, id, items);
this.ctx = ctx;
this.id = id;
this.items = items;
}
@Override
public AboutFragmentItem getItem(int index)
{
return items.get(index);
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
LayoutInflater vi = LayoutInflater.from(ctx);
convertView = vi.inflate(id, parent, false);
}
final AboutFragmentItem item = items.get(position);
if (item != null)
{
TextView title = (TextView) convertView.findViewById(R.id.AboutItemTitle);
TextView subtitle = (TextView) convertView.findViewById(R.id.AboutItemSubTitle);
if (title != null)
title.setText(item.getTitle());
if (subtitle != null)
subtitle.setText(item.getSubTitle());
}
return convertView;
}
}
}

View File

@ -0,0 +1,249 @@
package org.dolphinemu.dolphinemu.about;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.ActionBar.TabListener;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.os.Bundle;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.List;
/**
* Activity for the about menu, which displays info
* related to the CPU and GPU. Along with misc other info.
*/
public final class AboutActivity extends Activity implements TabListener
{
private ViewPager viewPager;
private final EGLHelper eglHelper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT);
// Represents an item in the multiple About fragments.
public static final class AboutFragmentItem
{
private final String title;
private final String subtitle;
public AboutFragmentItem(String title, String subtitle)
{
this.title = title;
this.subtitle = subtitle;
}
public String getTitle()
{
return title;
}
public String getSubTitle()
{
return subtitle;
}
}
// The adapter that manages the displaying of items in multiple About fragments.
public static final class InfoFragmentAdapter extends ArrayAdapter<AboutFragmentItem>
{
private final int id;
private final List<AboutFragmentItem> items;
public InfoFragmentAdapter(Context ctx, int id, List<AboutFragmentItem> items)
{
super(ctx, id, items);
this.id = id;
this.items = items;
}
@Override
public AboutFragmentItem getItem(int index)
{
return items.get(index);
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
LayoutInflater vi = LayoutInflater.from(getContext());
convertView = vi.inflate(id, parent, false);
}
final AboutFragmentItem item = items.get(position);
if (item != null)
{
TextView title = (TextView) convertView.findViewById(R.id.AboutItemTitle);
TextView subtitle = (TextView) convertView.findViewById(R.id.AboutItemSubTitle);
if (title != null)
title.setText(item.getTitle());
if (subtitle != null)
subtitle.setText(item.getSubTitle());
}
return convertView;
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Set the view pager
setContentView(R.layout.viewpager);
viewPager = (ViewPager) findViewById(R.id.pager);
// Initialize the ViewPager adapter.
final ViewPagerAdapter adapter = new ViewPagerAdapter(getFragmentManager());
viewPager.setAdapter(adapter);
// Set up the ActionBar
final ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.addTab(actionBar.newTab().setText(R.string.general).setTabListener(this));
actionBar.addTab(actionBar.newTab().setText(R.string.cpu).setTabListener(this));
actionBar.addTab(actionBar.newTab().setText(R.string.gles_two).setTabListener(this));
if (eglHelper.supportsGLES3())
actionBar.addTab(actionBar.newTab().setText(R.string.gles_three).setTabListener(this));
if (eglHelper.supportsOpenGL())
actionBar.addTab(actionBar.newTab().setText(R.string.desktop_gl).setTabListener(this));
// Set the page change listener
viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener()
{
@Override
public void onPageSelected(int position)
{
actionBar.setSelectedNavigationItem(position);
}
});
}
@Override
public void onDestroy()
{
super.onDestroy();
eglHelper.closeHelper();
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft)
{
// When the given tab is selected, switch to the corresponding page in the ViewPager.
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft)
{
// Do nothing.
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft)
{
// Do nothing.
}
/**
* {@link FragmentPagerAdapter} subclass responsible for handling
* the individual {@link Fragment}s within the {@link ViewPager}.
*/
private final class ViewPagerAdapter extends FragmentPagerAdapter
{
public ViewPagerAdapter(FragmentManager fm)
{
super(fm);
}
@Override
public Fragment getItem(int position)
{
if (position == 0)
{
return new DolphinInfoFragment();
}
else if (position == 1)
{
return new CPUInfoFragment(); // CPU
}
else if (position == 2) // GLES 2
{
return new GLES2InfoFragment();
}
else if (position == 3) // GLES 3 or OpenGL (depending on circumstances)
{
if (eglHelper.supportsGLES3())
return new GLES3InfoFragment();
else
return new GLInfoFragment(); // GLES3 not supported, but OpenGL is.
}
else if (position == 4) // OpenGL fragment
{
return new GLInfoFragment();
}
// This should never happen.
return null;
}
@Override
public int getCount()
{
if (eglHelper.supportsGLES3() && eglHelper.supportsOpenGL())
{
return 5;
}
else if (!eglHelper.supportsGLES3() && !eglHelper.supportsOpenGL())
{
return 3;
}
else // Either regular OpenGL or GLES3 isn't supported
{
return 4;
}
}
@Override
public CharSequence getPageTitle(int position)
{
switch (position)
{
case 0:
return getString(R.string.general);
case 1:
return getString(R.string.cpu);
case 2:
return getString(R.string.gles_two);
case 3:
return getString(R.string.gles_three);
case 4:
return getString(R.string.desktop_gl);
default: // Should never happen
return null;
}
}
}
}

View File

@ -0,0 +1,47 @@
package org.dolphinemu.dolphinemu.about;
import java.util.ArrayList;
import java.util.List;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.about.AboutActivity.AboutFragmentItem;
import org.dolphinemu.dolphinemu.utils.CPUHelper;
import android.app.ListFragment;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
/**
* {@link ListFragment class that is responsible
* for displaying information related to the CPU.
*/
public final class CPUInfoFragment extends ListFragment
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ListView rootView = (ListView) inflater.inflate(R.layout.gamelist_listview, container, false);
List<AboutActivity.AboutFragmentItem> items = new ArrayList<AboutActivity.AboutFragmentItem>();
CPUHelper cpuHelper = new CPUHelper(getActivity());
items.add(new AboutFragmentItem(getString(R.string.cpu_info), cpuHelper.getProcessorInfo()));
items.add(new AboutFragmentItem(getString(R.string.cpu_type), cpuHelper.getProcessorType()));
items.add(new AboutFragmentItem(getString(R.string.cpu_abi_one), Build.CPU_ABI));
items.add(new AboutFragmentItem(getString(R.string.cpu_abi_two), Build.CPU_ABI2));
items.add(new AboutFragmentItem(getString(R.string.num_cores), Integer.toString(cpuHelper.getNumCores())));
items.add(new AboutFragmentItem(getString(R.string.cpu_features), cpuHelper.getFeatures()));
items.add(new AboutFragmentItem(getString(R.string.cpu_hardware), Build.HARDWARE));
if (CPUHelper.isARM())
items.add(new AboutFragmentItem(getString(R.string.cpu_implementer), cpuHelper.getImplementer()));
AboutActivity.InfoFragmentAdapter adapter = new AboutActivity.InfoFragmentAdapter(getActivity(), R.layout.about_layout, items);
rootView.setAdapter(adapter);
return rootView;
}
}

View File

@ -0,0 +1,50 @@
/**
* Copyright 2013 Dolphin Emulator Project
* Licensed under GPLv2
* Refer to the license.txt file included.
*/
package org.dolphinemu.dolphinemu.about;
import android.app.ListFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.about.AboutActivity.AboutFragmentItem;
import org.dolphinemu.dolphinemu.about.AboutActivity.InfoFragmentAdapter;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
/**
* Represents the about screen.
*/
public final class DolphinInfoFragment extends ListFragment
{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ListView rootView = (ListView) inflater.inflate(R.layout.gamelist_listview, container, false);
EGLHelper eglHelper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT);
final String yes = getString(R.string.yes);
final String no = getString(R.string.no);
List<AboutFragmentItem> Input = new ArrayList<AboutFragmentItem>();
Input.add(new AboutFragmentItem(getString(R.string.build_revision), NativeLibrary.GetVersionString()));
Input.add(new AboutFragmentItem(getString(R.string.supports_gles3), eglHelper.supportsGLES3() ? yes : no));
Input.add(new AboutFragmentItem(getString(R.string.supports_neon), NativeLibrary.SupportsNEON() ? yes : no));
InfoFragmentAdapter adapter = new InfoFragmentAdapter(getActivity(), R.layout.about_layout, Input);
rootView.setAdapter(adapter);
rootView.setEnabled(false); // Makes the list view non-clickable.
return rootView;
}
}

View File

@ -0,0 +1,82 @@
/**
* Copyright 2013 Dolphin Emulator Project
* Licensed under GPLv2
* Refer to the license.txt file included.
*/
package org.dolphinemu.dolphinemu.about;
import android.app.ListFragment;
import android.opengl.GLES20;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.about.Limit.Type;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
import java.util.ArrayList;
import java.util.List;
/**
* {@link ListFragment} responsible for displaying
* information relating to OpenGL ES 2.
*/
public final class GLES2InfoFragment extends ListFragment
{
private final EGLHelper eglHelper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT);
private final Limit[] Limits = {
new Limit("Vendor", GLES20.GL_VENDOR, Type.STRING),
new Limit("Version", GLES20.GL_VERSION, Type.STRING),
new Limit("Renderer", GLES20.GL_RENDERER, Type.STRING),
new Limit("GLSL version", GLES20.GL_SHADING_LANGUAGE_VERSION, Type.STRING),
// GLES 2.0 Limits
//new Limit("Aliased Point Size", GLES20.GL_ALIASED_POINT_SIZE_RANGE, Type.INTEGER_RANGE),
//new Limit("Aliased Line Width ", GLES20.GL_ALIASED_LINE_WIDTH_RANGE, Type.INTEGER_RANGE),
new Limit("Max Texture Size", GLES20.GL_MAX_TEXTURE_SIZE, Type.INTEGER),
//new Limit("Viewport Dimensions", GLES20.GL_MAX_VIEWPORT_DIMS, Type.INTEGER_RANGE),
new Limit("Subpixel Bits", GLES20.GL_SUBPIXEL_BITS, Type.INTEGER),
new Limit("Max Vertex Attributes", GLES20.GL_MAX_VERTEX_ATTRIBS, Type.INTEGER),
new Limit("Max Vertex Uniform Vectors", GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, Type.INTEGER),
new Limit("Max Varying Vectors", GLES20.GL_MAX_VARYING_VECTORS, Type.INTEGER),
new Limit("Max Combined Texture Units", GLES20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, Type.INTEGER),
new Limit("Max Vertex Texture Units", GLES20.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, Type.INTEGER),
new Limit("Max Texture Units", GLES20.GL_MAX_TEXTURE_IMAGE_UNITS, Type.INTEGER),
new Limit("Max Fragment Uniform Vectors", GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, Type.INTEGER),
new Limit("Max Cubemap Texture Size", GLES20.GL_MAX_CUBE_MAP_TEXTURE_SIZE, Type.INTEGER),
new Limit("Shader Binary Formats", GLES20.GL_NUM_SHADER_BINARY_FORMATS, Type.INTEGER),
new Limit("Max Framebuffer Size", GLES20.GL_MAX_RENDERBUFFER_SIZE, Type.INTEGER),
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ListView rootView = (ListView) inflater.inflate(R.layout.gamelist_listview, container, false);
List<AboutActivity.AboutFragmentItem> Input = new ArrayList<AboutActivity.AboutFragmentItem>();
for (Limit limit : Limits)
{
Log.i("GLES2InfoFragment", "Getting enum " + limit.name);
Input.add(new AboutActivity.AboutFragmentItem(limit.name, limit.GetValue(eglHelper)));
}
// Get extensions manually
String[] extensions = eglHelper.glGetString(GLES20.GL_EXTENSIONS).split(" ");
StringBuilder extensionsBuilder = new StringBuilder();
for (String extension : extensions)
{
extensionsBuilder.append(extension).append('\n');
}
Input.add(new AboutActivity.AboutFragmentItem("OpenGL ES 2.0 Extensions", extensionsBuilder.toString()));
AboutActivity.InfoFragmentAdapter adapter = new AboutActivity.InfoFragmentAdapter(getActivity(), R.layout.about_layout, Input);
rootView.setAdapter(adapter);
return rootView;
}
}

View File

@ -0,0 +1,114 @@
/**
* Copyright 2013 Dolphin Emulator Project
* Licensed under GPLv2
* Refer to the license.txt file included.
*/
package org.dolphinemu.dolphinemu.about;
import android.app.ListFragment;
import android.opengl.GLES30;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.about.Limit.Type;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
import java.util.ArrayList;
import java.util.List;
/**
* {@link ListFragment} responsible for displaying
* information relating to OpenGL ES 3.
*/
public final class GLES3InfoFragment extends ListFragment
{
private final EGLHelper eglHelper = new EGLHelper(EGLHelper.EGL_OPENGL_ES3_BIT_KHR);
private final Limit[] Limits = {
new Limit("Vendor", GLES30.GL_VENDOR, Type.STRING),
new Limit("Version", GLES30.GL_VERSION, Type.STRING),
new Limit("Renderer", GLES30.GL_RENDERER, Type.STRING),
new Limit("GLSL version", GLES30.GL_SHADING_LANGUAGE_VERSION, Type.STRING),
// GLES 2.0 Limits
new Limit("Aliased Point Size", GLES30.GL_ALIASED_POINT_SIZE_RANGE, Type.INTEGER_RANGE),
new Limit("Aliased Line Width ", GLES30.GL_ALIASED_LINE_WIDTH_RANGE, Type.INTEGER_RANGE),
new Limit("Max Texture Size", GLES30.GL_MAX_TEXTURE_SIZE, Type.INTEGER),
new Limit("Viewport Dimensions", GLES30.GL_MAX_VIEWPORT_DIMS, Type.INTEGER_RANGE),
new Limit("Subpixel Bits", GLES30.GL_SUBPIXEL_BITS, Type.INTEGER),
new Limit("Max Vertex Attributes", GLES30.GL_MAX_VERTEX_ATTRIBS, Type.INTEGER),
new Limit("Max Vertex Uniform Vectors", GLES30.GL_MAX_VERTEX_UNIFORM_VECTORS, Type.INTEGER),
new Limit("Max Varying Vectors", GLES30.GL_MAX_VARYING_VECTORS, Type.INTEGER),
new Limit("Max Combined Texture Units", GLES30.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, Type.INTEGER),
new Limit("Max Vertex Texture Units", GLES30.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, Type.INTEGER),
new Limit("Max Texture Units", GLES30.GL_MAX_TEXTURE_IMAGE_UNITS, Type.INTEGER),
new Limit("Max Fragment Uniform Vectors", GLES30.GL_MAX_FRAGMENT_UNIFORM_VECTORS, Type.INTEGER),
new Limit("Max Cubemap Texture Size", GLES30.GL_MAX_CUBE_MAP_TEXTURE_SIZE, Type.INTEGER),
new Limit("Shader Binary Formats", GLES30.GL_NUM_SHADER_BINARY_FORMATS, Type.INTEGER),
new Limit("Max Framebuffer Size", GLES30.GL_MAX_RENDERBUFFER_SIZE, Type.INTEGER),
// GLES 3.0 limits
new Limit("Max 3D Texture size", GLES30.GL_MAX_3D_TEXTURE_SIZE, Type.INTEGER),
new Limit("Max Element Vertices", GLES30.GL_MAX_ELEMENTS_VERTICES, Type.INTEGER),
new Limit("Max Element Indices", GLES30.GL_MAX_ELEMENTS_INDICES, Type.INTEGER),
new Limit("Max Draw Buffers", GLES30.GL_MAX_DRAW_BUFFERS, Type.INTEGER),
new Limit("Max Fragment Uniform Components", GLES30.GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, Type.INTEGER),
new Limit("Max Vertex Uniform Components", GLES30.GL_MAX_VERTEX_UNIFORM_COMPONENTS, Type.INTEGER),
new Limit("Number of Extensions", GLES30.GL_NUM_EXTENSIONS, Type.INTEGER),
new Limit("Max Array Texture Layers", GLES30.GL_MAX_ARRAY_TEXTURE_LAYERS, Type.INTEGER),
new Limit("Min Program Texel Offset", GLES30.GL_MIN_PROGRAM_TEXEL_OFFSET, Type.INTEGER),
new Limit("Max Program Texel Offset", GLES30.GL_MAX_PROGRAM_TEXEL_OFFSET, Type.INTEGER),
new Limit("Max Varying Components", GLES30.GL_MAX_VARYING_COMPONENTS, Type.INTEGER),
new Limit("Max TF Varying Length", GLES30.GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, Type.INTEGER),
new Limit("Max TF Separate Components", GLES30.GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, Type.INTEGER),
new Limit("Max TF Interleaved Components", GLES30.GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, Type.INTEGER),
new Limit("Max TF Separate Attributes", GLES30.GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, Type.INTEGER),
new Limit("Max Color Attachments", GLES30.GL_MAX_COLOR_ATTACHMENTS, Type.INTEGER),
new Limit("Max Samples", GLES30.GL_MAX_SAMPLES, Type.INTEGER),
new Limit("Max Vertex UBOs", GLES30.GL_MAX_VERTEX_UNIFORM_BLOCKS, Type.INTEGER),
new Limit("Max Fragment UBOs", GLES30.GL_MAX_FRAGMENT_UNIFORM_BLOCKS, Type.INTEGER),
new Limit("Max Combined UBOs", GLES30.GL_MAX_COMBINED_UNIFORM_BLOCKS, Type.INTEGER),
new Limit("Max Uniform Buffer Bindings", GLES30.GL_MAX_UNIFORM_BUFFER_BINDINGS, Type.INTEGER),
new Limit("Max UBO Size", GLES30.GL_MAX_UNIFORM_BLOCK_SIZE, Type.INTEGER),
new Limit("Max Combined Vertex Uniform Components", GLES30.GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS, Type.INTEGER),
new Limit("Max Combined Fragment Uniform Components", GLES30.GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, Type.INTEGER),
new Limit("UBO Alignment", GLES30.GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, Type.INTEGER),
new Limit("Max Vertex Output Components", GLES30.GL_MAX_VERTEX_OUTPUT_COMPONENTS, Type.INTEGER),
new Limit("Max Fragment Input Components", GLES30.GL_MAX_FRAGMENT_INPUT_COMPONENTS, Type.INTEGER),
new Limit("Max Server Wait Timeout", GLES30.GL_MAX_SERVER_WAIT_TIMEOUT, Type.INTEGER),
new Limit("Program Binary Formats", GLES30.GL_NUM_PROGRAM_BINARY_FORMATS, Type.INTEGER),
new Limit("Max Element Index", GLES30.GL_MAX_ELEMENT_INDEX, Type.INTEGER),
new Limit("Sample Counts", GLES30.GL_NUM_SAMPLE_COUNTS, Type.INTEGER),
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ListView rootView = (ListView) inflater.inflate(R.layout.gamelist_listview, container, false);
List<AboutActivity.AboutFragmentItem> Input = new ArrayList<AboutActivity.AboutFragmentItem>();
for (Limit limit : Limits)
{
Log.i("GLES3InfoFragment", "Getting enum " + limit.name);
Input.add(new AboutActivity.AboutFragmentItem(limit.name, limit.GetValue(eglHelper)));
}
// Get extensions manually
int numExtensions = eglHelper.glGetInteger(GLES30.GL_NUM_EXTENSIONS);
StringBuilder extensionsBuilder = new StringBuilder();
for (int i = 0; i < numExtensions; i++)
{
extensionsBuilder.append(eglHelper.glGetStringi(GLES30.GL_EXTENSIONS, i)).append('\n');
}
Input.add(new AboutActivity.AboutFragmentItem("OpenGL ES 3.0 Extensions", extensionsBuilder.toString()));
AboutActivity.InfoFragmentAdapter adapter = new AboutActivity.InfoFragmentAdapter(getActivity(), R.layout.about_layout, Input);
rootView.setAdapter(adapter);
return rootView;
}
}

View File

@ -0,0 +1,69 @@
/**
* Copyright 2013 Dolphin Emulator Project
* Licensed under GPLv2
* Refer to the license.txt file included.
*/
package org.dolphinemu.dolphinemu.about;
import android.app.ListFragment;
import android.opengl.GLES20;
import android.opengl.GLES30;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.about.Limit.Type;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
import java.util.ArrayList;
import java.util.List;
import javax.microedition.khronos.opengles.GL10;
/**
* {@link ListFragment} responsible for displaying
* information relating to OpenGL.
*/
public final class GLInfoFragment extends ListFragment
{
private final EGLHelper eglHelper = new EGLHelper(EGLHelper.EGL_OPENGL_BIT);
private final Limit[] Limits = {
new Limit("Vendor", GL10.GL_VENDOR, Type.STRING),
new Limit("Version", GL10.GL_VERSION, Type.STRING),
new Limit("Renderer", GL10.GL_RENDERER, Type.STRING),
new Limit("GLSL version", GLES20.GL_SHADING_LANGUAGE_VERSION, Type.STRING),
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
ListView rootView = (ListView) inflater.inflate(R.layout.gamelist_listview, container, false);
List<AboutActivity.AboutFragmentItem> Input = new ArrayList<AboutActivity.AboutFragmentItem>();
for (Limit limit : Limits)
{
Log.i("GLInfoFragment", "Getting enum " + limit.name);
Input.add(new AboutActivity.AboutFragmentItem(limit.name, limit.GetValue(eglHelper)));
}
// Get extensions manually
int numExtensions = eglHelper.glGetInteger(GLES30.GL_NUM_EXTENSIONS);
StringBuilder extensionsBuilder = new StringBuilder();
for (int i = 0; i < numExtensions; i++)
{
extensionsBuilder.append(eglHelper.glGetStringi(GL10.GL_EXTENSIONS, i)).append('\n');
}
Input.add(new AboutActivity.AboutFragmentItem("OpenGL Extensions", extensionsBuilder.toString()));
AboutActivity.InfoFragmentAdapter adapter = new AboutActivity.InfoFragmentAdapter(getActivity(), R.layout.about_layout, Input);
rootView.setAdapter(adapter);
return rootView;
}
}

View File

@ -0,0 +1,65 @@
package org.dolphinemu.dolphinemu.about;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
final class Limit
{
/**
* Possible types a Limit can be.
*/
enum Type
{
/** Generic string */
STRING,
/** Integer constant */
INTEGER,
/** Range of integers */
INTEGER_RANGE,
}
/** Name of this limit */
public final String name;
/** The GL constant that represents this limit.*/
public final int glEnum;
/** The {@link Type} of this limit. */
public final Type type;
/**
* Constructor
*
* @param name Name of the limit.
* @param glEnum The GL constant that represents this limit.
* @param type The {@link Type} of this limit.
*/
public Limit(String name, int glEnum, Type type)
{
this.name = name;
this.glEnum = glEnum;
this.type = type;
}
/**
* Retrieves the information represented by this limit.
*
* @param context {@link EGLHelper} context to retrieve the limit with.
*
* @return the information represented by this limit.
*/
public String GetValue(EGLHelper context)
{
if (type == Type.INTEGER)
{
return Integer.toString(context.glGetInteger(glEnum));
}
else if (type == Type.INTEGER_RANGE)
{
int[] data = new int[2];
context.getGL().glGetIntegerv(glEnum, data, 0);
return String.format("[%d, %d]", data[0], data[1]);
}
// If neither of the above type, assume it's a string.
return context.glGetString(glEnum);
}
}

View File

@ -22,10 +22,12 @@ import android.view.WindowManager.LayoutParams;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.settings.input.InputConfigFragment; import org.dolphinemu.dolphinemu.settings.input.InputConfigFragment;
import org.dolphinemu.dolphinemu.settings.video.VideoSettingsFragment; import org.dolphinemu.dolphinemu.utils.EGLHelper;
import java.util.List; import java.util.List;
import javax.microedition.khronos.opengles.GL10;
/** /**
* This is the activity where all of the emulation handling happens. * This is the activity where all of the emulation handling happens.
* This activity is responsible for displaying the SurfaceView that we render to. * This activity is responsible for displaying the SurfaceView that we render to.
@ -65,11 +67,7 @@ public final class EmulationActivity extends Activity
// This bug is fixed in Qualcomm driver v53 // This bug is fixed in Qualcomm driver v53
// Mali isn't affected by this bug. // Mali isn't affected by this bug.
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
if (sharedPrefs.getString("gpuPref", "Software Rendering").equals("OGL") if (hasBuggedDriverDimensions())
&& VideoSettingsFragment.SupportsGLES3()
&& VideoSettingsFragment.m_GLVendor != null
&& VideoSettingsFragment.m_GLVendor.equals("Qualcomm")
&& VideoSettingsFragment.m_QualcommVersion < 53.0f)
NativeLibrary.SetDimensions((int)screenHeight, (int)screenWidth); NativeLibrary.SetDimensions((int)screenHeight, (int)screenWidth);
else else
NativeLibrary.SetDimensions((int)screenWidth, (int)screenHeight); NativeLibrary.SetDimensions((int)screenWidth, (int)screenHeight);
@ -330,4 +328,39 @@ public final class EmulationActivity extends Activity
return true; return true;
} }
// For handling bugged driver dimensions (applies mainly to Qualcomm devices)
private boolean hasBuggedDriverDimensions()
{
final EGLHelper eglHelper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT);
final String vendor = eglHelper.getGL().glGetString(GL10.GL_VENDOR);
final String version = eglHelper.getGL().glGetString(GL10.GL_VERSION);
final String renderer = eglHelper.getGL().glGetString(GL10.GL_RENDERER);
if (sharedPrefs.getString("gpuPref", "Software Rendering").equals("OGL")
&& eglHelper.supportsGLES3()
&& vendor.equals("Qualcomm")
&& renderer.equals("Adreno (TM) 3"))
{
final int start = version.indexOf("V@") + 2;
final StringBuilder versionBuilder = new StringBuilder();
for (int i = start; i < version.length(); i++)
{
char c = version.charAt(i);
// End of numeric portion of version string.
if (c == ' ')
break;
versionBuilder.append(c);
}
if (Float.parseFloat(versionBuilder.toString()) < 53.0f)
return true;
}
return false;
}
} }

View File

@ -23,9 +23,10 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ListView; import android.widget.ListView;
import org.dolphinemu.dolphinemu.AboutFragment;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.about.AboutActivity;
import org.dolphinemu.dolphinemu.folderbrowser.FolderBrowser; import org.dolphinemu.dolphinemu.folderbrowser.FolderBrowser;
import org.dolphinemu.dolphinemu.settings.PrefsActivity; import org.dolphinemu.dolphinemu.settings.PrefsActivity;
import org.dolphinemu.dolphinemu.sidemenu.SideMenuAdapter; import org.dolphinemu.dolphinemu.sidemenu.SideMenuAdapter;
@ -119,11 +120,7 @@ public final class GameListActivity extends Activity
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.device_compat_warning); builder.setTitle(R.string.device_compat_warning);
builder.setMessage(R.string.device_compat_warning_msg); builder.setMessage(R.string.device_compat_warning_msg);
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { builder.setPositiveButton(R.string.yes, null);
public void onClick(DialogInterface dialog, int which) {
// Do Nothing. Just create the Yes button
}
});
builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) public void onClick(DialogInterface dialog, int which)
{ {
@ -184,13 +181,8 @@ public final class GameListActivity extends Activity
case 3: // About case 3: // About
{ {
mCurFragmentNum = 3; Intent intent = new Intent(this, AboutActivity.class);
final AboutFragment aboutFragment = new AboutFragment(); startActivity(intent);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, aboutFragment);
ft.addToBackStack(null);
ft.commit();
invalidateOptionsMenu();
} }
break; break;
@ -261,7 +253,8 @@ public final class GameListActivity extends Activity
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.clear_game_list); builder.setTitle(R.string.clear_game_list);
builder.setMessage(getString(R.string.clear_game_list_confirm)); builder.setMessage(getString(R.string.clear_game_list_confirm));
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener(){ builder.setNegativeButton(R.string.no, null);
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) public void onClick(DialogInterface dialog, int which)
{ {
String directories = NativeLibrary.GetConfig("Dolphin.ini", "General", "GCMPathes", "0"); String directories = NativeLibrary.GetConfig("Dolphin.ini", "General", "GCMPathes", "0");
@ -279,12 +272,6 @@ public final class GameListActivity extends Activity
((GameListFragment) getFragmentManager().findFragmentById(R.id.content_frame)).clearGameList(); ((GameListFragment) getFragmentManager().findFragmentById(R.id.content_frame)).clearGameList();
} }
}); });
builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
// Do nothing. This just make "No" appear.
}
});
builder.show(); builder.show();
} }

View File

@ -41,7 +41,7 @@ public final class PrefsActivity extends Activity implements ActionBar.TabListen
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// Set the ViewPager. // Set the ViewPager.
setContentView(R.layout.prefs_viewpager); setContentView(R.layout.viewpager);
mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager = (ViewPager) findViewById(R.id.pager);
// Set the ViewPager adapter. // Set the ViewPager adapter.

View File

@ -15,9 +15,10 @@ import android.preference.ListPreference;
import android.preference.PreferenceFragment; import android.preference.PreferenceFragment;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import org.dolphinemu.dolphinemu.R;
import javax.microedition.khronos.egl.*; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10;
/** /**
@ -25,173 +26,9 @@ import javax.microedition.khronos.opengles.GL10;
*/ */
public final class VideoSettingsFragment extends PreferenceFragment public final class VideoSettingsFragment extends PreferenceFragment
{ {
public static String m_GLVersion; private final EGLHelper eglHelper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT);
public static String m_GLVendor; private final String vendor = eglHelper.getGL().glGetString(GL10.GL_VENDOR);
public static String m_GLRenderer; private final String version = eglHelper.getGL().glGetString(GL10.GL_VERSION);
public static String m_GLExtensions;
public static boolean m_SupportsGLES3 = false;
public static float m_QualcommVersion;
public static boolean m_Inited = false;
/**
* Class which provides a means to retrieve various
* info about the OpenGL ES support/features within a device.
*/
public static final class VersionCheck
{
private EGL10 mEGL;
private EGLDisplay mEGLDisplay;
private EGLConfig[] mEGLConfigs;
private EGLConfig mEGLConfig;
private EGLContext mEGLContext;
private EGLSurface mEGLSurface;
private GL10 mGL;
String mThreadOwner;
public VersionCheck()
{
int[] version = new int[2];
int[] attribList = new int[] {
EGL10.EGL_WIDTH, 1,
EGL10.EGL_HEIGHT, 1,
EGL10.EGL_RENDERABLE_TYPE, 4,
EGL10.EGL_NONE
};
int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int[] ctx_attribs = new int[] {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL10.EGL_NONE
};
// No error checking performed, minimum required code to elucidate logic
mEGL = (EGL10) EGLContext.getEGL();
mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
mEGL.eglInitialize(mEGLDisplay, version);
mEGLConfig = chooseConfig(); // Choosing a config is a little more complicated
mEGLContext = mEGL.eglCreateContext(mEGLDisplay, mEGLConfig, EGL10.EGL_NO_CONTEXT, ctx_attribs);
mEGLSurface = mEGL.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, attribList);
mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
mGL = (GL10) mEGLContext.getGL();
// Record thread owner of OpenGL context
mThreadOwner = Thread.currentThread().getName();
}
/**
* Gets the OpenGL ES version string.
*
* @return the OpenGL ES version string.
*/
public String getVersion()
{
return mGL.glGetString(GL10.GL_VERSION);
}
/**
* Gets the OpenGL ES vendor string.
*
* @return the OpenGL ES vendor string.
*/
public String getVendor()
{
return mGL.glGetString(GL10.GL_VENDOR);
}
/**
* Gets the name of the OpenGL ES renderer.
*
* @return the name of the OpenGL ES renderer.
*/
public String getRenderer()
{
return mGL.glGetString(GL10.GL_RENDERER);
}
/**
* Gets the extension that the device supports
*
* @return String containing the extensions
*/
public String getExtensions()
{
return mGL.glGetString(GL10.GL_EXTENSIONS);
}
private EGLConfig chooseConfig()
{
int[] attribList = new int[] {
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_NONE
};
// Grab how many configs there are
int[] numConfig = new int[1];
mEGL.eglChooseConfig(mEGLDisplay, attribList, null, 0, numConfig);
int configSize = numConfig[0];
mEGLConfigs = new EGLConfig[configSize];
mEGL.eglChooseConfig(mEGLDisplay, attribList, mEGLConfigs, configSize, numConfig);
// Check if one config supports GLES3
for (int i = 0; i < configSize; ++i)
{
int[] value = new int[1];
boolean retval = mEGL.eglGetConfigAttrib(mEGLDisplay, mEGLConfigs[i], EGL10.EGL_RENDERABLE_TYPE, value);
if (retval && (value[0] & (1 << 6)) != 0)
{
m_SupportsGLES3 = true;
break;
}
}
return mEGLConfigs[0]; // Best match is probably the first configuration
}
}
/**
* Checks if this device supports OpenGL ES 3.
*
* @return true if this device supports OpenGL ES 3; false otherwise.
*/
public static boolean SupportsGLES3()
{
if (!m_Inited)
{
VersionCheck mbuffer = new VersionCheck();
m_GLVersion = mbuffer.getVersion();
m_GLVendor = mbuffer.getVendor();
m_GLRenderer = mbuffer.getRenderer();
m_GLExtensions = mbuffer.getExtensions();
// Checking for OpenGL ES 3 support for certain Qualcomm devices.
if (m_GLVendor != null && m_GLVendor.equals("Qualcomm"))
{
if (m_GLRenderer.contains("Adreno (TM) 3"))
{
int mVStart = m_GLVersion.indexOf("V@") + 2;
int mVEnd = 0;
for (int a = mVStart; a < m_GLVersion.length(); ++a)
{
if (m_GLVersion.charAt(a) == ' ')
{
mVEnd = a;
break;
}
}
m_QualcommVersion = Float.parseFloat(m_GLVersion.substring(mVStart, mVEnd));
}
}
m_Inited = true;
}
return m_SupportsGLES3;
}
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState)
@ -205,7 +42,7 @@ public final class VideoSettingsFragment extends PreferenceFragment
// Setting valid video backends. // Setting valid video backends.
// //
final ListPreference videoBackends = (ListPreference) findPreference("gpuPref"); final ListPreference videoBackends = (ListPreference) findPreference("gpuPref");
final boolean deviceSupportsGLES3 = SupportsGLES3(); final boolean deviceSupportsGLES3 = eglHelper.supportsGLES3();
if (deviceSupportsGLES3) if (deviceSupportsGLES3)
{ {
@ -264,10 +101,9 @@ public final class VideoSettingsFragment extends PreferenceFragment
//mainScreen.getPreference(4).setEnabled(false); //mainScreen.getPreference(4).setEnabled(false);
// Create an alert telling them that their phone sucks // Create an alert telling them that their phone sucks
if (VideoSettingsFragment.SupportsGLES3() if (eglHelper.supportsGLES3()
&& VideoSettingsFragment.m_GLVendor != null && vendor.equals("Qualcomm")
&& VideoSettingsFragment.m_GLVendor.equals("Qualcomm") && getQualcommVersion() == 14.0f)
&& VideoSettingsFragment.m_QualcommVersion == 14.0f)
{ {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.device_compat_warning); builder.setTitle(R.string.device_compat_warning);
@ -295,4 +131,23 @@ public final class VideoSettingsFragment extends PreferenceFragment
} }
}); });
} }
private float getQualcommVersion()
{
final int start = version.indexOf("V@") + 2;
final StringBuilder versionBuilder = new StringBuilder();
for (int i = start; i < version.length(); i++)
{
char c = version.charAt(i);
// End of numeric portion of version string.
if (c == ' ')
break;
versionBuilder.append(c);
}
return Float.parseFloat(versionBuilder.toString());
}
} }

View File

@ -0,0 +1,400 @@
package org.dolphinemu.dolphinemu.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import org.dolphinemu.dolphinemu.R;
import android.content.Context;
import android.os.Build;
import android.util.Log;
/**
* Utility class for retrieving information
* from a device's CPU.
*/
public final class CPUHelper
{
private int revision;
private int architecture;
private int variant;
private int numCores;
private String implementerID = "N/A";
private String part = "N/A";
private String hardware = "N/A";
private String processorInfo = "N/A";
private String features = "N/A";
private final Context ctx;
/**
* Constructor
*
* @param ctx The current {@link Context}.
*/
public CPUHelper(Context ctx)
{
this.ctx = ctx;
try
{
// TODO: Should do other architectures as well (x86 and MIPS).
// Can do differentiating between platforms by using
// android.os.Build.CPU_ABI.
//
// CPU_ABI.contains("armeabi") == get ARM info.
// CPU_ABI.contains("x86") == get x86 info.
// CPU_ABI.contains("mips") == get MIPS info.
//
// However additional testing should be done across devices,
// I highly doubt /proc/cpuinfo retains the same formatting
// on different architectures.
//
// If push comes to shove, we can simply spit out the cpuinfo
// contents. I would like to avoid this if possible, however.
if (Build.CPU_ABI.contains("arm"))
{
getARMInfo();
}
else
{
Log.e("CPUHelper", "CPU architecture not supported yet.");
}
}
catch (IOException ioe)
{
Log.e("CPUHelper", ioe.getMessage());
}
}
/**
* Gets the revision number of the CPU.
*
* @return the revision number of the CPU.
*/
public int getRevision()
{
return revision;
}
/**
* Gets the architecture number of the CPU.
*
* @return the architecture number of the CPU.
*/
public int getArchitecture()
{
return architecture;
}
/**
* Gets the CPU variant number.
*
* @return the CPU variant number.
*/
public int getVariant()
{
return variant;
}
/**
* Gets the total number of cores in the CPU.
*
* @return the total number of cores in the CPU.
*/
public int getNumCores()
{
return numCores;
}
/**
* Gets the name of the implementer of the CPU.
*
* @return the name of the implementer of the CPU.
*/
public String getImplementer()
{
return implementerID;
}
/**
* Gets the specific processor type of the CPU.
*
* @return the specific processor type.
*/
public String getProcessorType()
{
return part;
}
/**
* Gets the internal name of the hardware.
*
* @return the internal name of the hardware.
*/
public String getHardware()
{
return hardware;
}
/**
* Get the processor info string.
*
* @return the processor info string.
*/
public String getProcessorInfo()
{
return processorInfo;
}
/**
* Gets the features supported by the CPU.
*
* @return the features supported by the CPU.
*/
public String getFeatures()
{
return features;
}
/**
* Whether or not this CPU is using the ARM architecture.
*
* @return true if this CPU uses the ARM architecture; false otherwise.
*/
public static boolean isARM()
{
return Build.CPU_ABI.contains("arm");
}
/**
* Whether or not this CPU is using the x86 architecture.
*
* @return true if this CPU uses the x86 architecture; false otherwise.
*/
public static boolean isX86()
{
return Build.CPU_ABI.contains("x86");
}
/**
* Whether or not this CPU is using the MIPS architecture.
*
* @return true if this CPU uses the MIPS architecture; false otherwise.
*/
public static boolean isMIPS()
{
return Build.CPU_ABI.contains("mips");
}
// Retrieves information for ARM CPUs.
private void getARMInfo() throws IOException
{
File info = new File("/proc/cpuinfo");
if (info.exists())
{
BufferedReader br = new BufferedReader(new FileReader(info));
String line;
while ((line = br.readLine()) != null)
{
if (line.contains("Processor\t:"))
{
this.processorInfo = parseLine(line);
}
else if (line.contains("Hardware\t:"))
{
this.hardware = parseLine(line);
}
else if (line.contains("Features\t:"))
{
this.features = parseLine(line);
}
else if (line.contains("CPU implementer\t:"))
{
this.implementerID = parseArmID(Integer.decode(parseLine(line)));
}
// Intentional lack of "\t:" sometimes the tab isn't present.
else if (line.contains("CPU architecture"))
{
this.architecture = Integer.decode(parseLine(line));
}
else if (line.contains("CPU part\t:"))
{
this.part = parseArmPartNumber(Integer.decode(parseLine(line)));
}
else if (line.contains("CPU revision\t:"))
{
this.revision = Integer.decode(parseLine(line));
}
else if (line.contains("CPU variant\t:"))
{
this.variant = Integer.decode(parseLine(line));
}
else if (line.contains("processor\t:")) // Lower case indicates a specific core number
{
this.numCores++;
}
}
br.close();
}
}
// Basic function for parsing cpuinfo format strings.
// cpuinfo format strings consist of [label:info] parts.
// We only want to retrieve the info portion so we split
// them using ':' as a delimeter.
private String parseLine(String line)
{
String[] temp = line.split(":");
if (temp.length != 2)
return "N/A";
return temp[1].trim();
}
// Parses an ARM CPU ID.
private String parseArmID(int id)
{
switch (id)
{
case 0x41:
return "ARM Limited";
case 0x44:
return "Digital Equipment Corporation";
case 0x4D:
return "Freescale Semiconductor Inc.";
case 0x51:
return "Qualcomm Inc.";
case 0x56:
return "Marvell Semiconductor Inc.";
case 0x69:
return "Intel Corporation";
default:
return "N/A";
}
}
// Parses the ARM CPU Part number.
private String parseArmPartNumber(int partNum)
{
switch (partNum)
{
// Qualcomm part numbers.
case 0x00F:
return "Qualcomm Scorpion";
case 0x02D:
return "Qualcomm Dual Scorpion";
case 0x04D:
return "Qualcomm Dual Krait";
case 0x06F:
return "Qualcomm Quad Krait";
// Marvell Semiconductor part numbers
case 0x131:
return "Marvell Feroceon";
case 0x581:
return "Marvell PJ4/PJ4b";
case 0x584:
return "Marvell Dual PJ4/PJ4b";
// Official ARM part numbers.
case 0x920:
return "ARM920";
case 0x922:
return "ARM922";
case 0x926:
return "ARM926";
case 0x940:
return "ARM940";
case 0x946:
return "ARM946";
case 0x966:
return "ARM966";
case 0x968:
return "ARM968";
case 0xB02:
return "ARM11 MPCore";
case 0xB36:
return "ARM1136";
case 0xB56:
return "ARM1156";
case 0xB76:
return "ARM1176";
case 0xC05:
return "ARM Cortex A5";
case 0xC07:
return "ARM Cortex-A7 MPCore";
case 0xC08:
return "ARM Cortex A8";
case 0xC09:
return "ARM Cortex A9";
case 0xC0C:
return "ARM Cortex A12";
case 0xC0F:
return "ARM Cortex A15";
case 0xC14:
return "ARM Cortex R4";
case 0xC15:
return "ARM Cortex R5";
case 0xC20:
return "ARM Cortex M0";
case 0xC21:
return "ARM Cortex M1";
case 0xC23:
return "ARM Cortex M3";
case 0xC24:
return "ARM Cortex M4";
case 0xC60:
return "ARM Cortex M0+";
case 0xD03:
return "ARM Cortex A53";
case 0xD07:
return "ARM Cortex A57 MPCore";
default: // Unknown/Not yet added to list.
return String.format(ctx.getString(R.string.unknown_part_num), partNum);
}
}
}

View File

@ -0,0 +1,360 @@
/**
* Copyright 2013 Dolphin Emulator Project
* Licensed under GPLv2
* Refer to the license.txt file included.
*/
package org.dolphinemu.dolphinemu.utils;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES30;
import android.util.Log;
/**
* Utility class that abstracts all the stuff about
* EGL initialization out of the way if all that is
* wanted is to query the underlying GL API for information.
*/
public final class EGLHelper
{
private final EGL10 mEGL;
private final EGLDisplay mDisplay;
private EGLConfig[] mEGLConfigs;
private EGLContext mEGLContext;
private EGLSurface mEGLSurface;
private GL10 mGL;
// GL support flags
private boolean supportGL;
private boolean supportGLES2;
private boolean supportGLES3;
// Renderable type bitmasks
public static final int EGL_OPENGL_ES_BIT = 0x0001;
public static final int EGL_OPENGL_ES2_BIT = 0x0004;
public static final int EGL_OPENGL_BIT = 0x0008;
public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040;
/**
* Constructor
* <p>
* Initializes the underlying {@link EGLSurface} with a width and height of 1.
* This is useful if all you need to use this class for is to query information
* from specific API contexts.
*
* @param renderableType Bitmask indicating which types of client API contexts
* the framebuffer config must support.
*/
public EGLHelper(int renderableType)
{
this(1, 1, renderableType);
}
/**
* Constructor
*
* @param width Width of the underlying {@link EGLSurface}.
* @param height Height of the underlying {@link EGLSurface}.
* @param renderableType Bitmask indicating which types of client API contexts
* the framebuffer config must support.
*/
public EGLHelper(int width, int height, int renderableType)
{
// Initialize handle to an EGL display.
mEGL = (EGL10) EGLContext.getEGL();
mDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
// If a display is present, initialize EGL.
if (mDisplay != EGL10.EGL_NO_DISPLAY)
{
int[] version = new int[2];
if (mEGL.eglInitialize(mDisplay, version))
{
// Detect supported GL APIs, initialize configs, etc.
detect();
// Create context and surface
create(width, height, renderableType);
}
else
{
Log.e("EGLHelper", "Error initializing EGL.");
}
}
else
{
Log.e("EGLHelper", "Error initializing EGL display.");
}
}
/**
* Releases all resources associated with this helper.
* <p>
* This should be called whenever this helper is no longer needed.
*/
public void closeHelper()
{
mEGL.eglTerminate(mDisplay);
}
/**
* Gets information through EGL.<br/>
* <p>
* Index 0: Vendor <br/>
* Index 1: Version <br/>
* Index 2: Renderer <br/>
* Index 3: Extensions <br/>
*
* @return information retrieved through EGL.
*/
public String[] getEGLInfo()
{
String[] info = {
mGL.glGetString(GL10.GL_VENDOR),
mGL.glGetString(GL10.GL_VERSION),
mGL.glGetString(GL10.GL_RENDERER),
mGL.glGetString(GL10.GL_EXTENSIONS),
};
return info;
}
/**
* Whether or not this device supports OpenGL.
*
* @return true if this device supports OpenGL; false otherwise.
*/
public boolean supportsOpenGL()
{
return supportGL;
}
/**
* Whether or not this device supports OpenGL ES 2.
* <br/>
* Note that if this returns true, then OpenGL ES 1 is also supported.
*
* @return true if this device supports OpenGL ES 2; false otherwise.
*/
public boolean supportsGLES2()
{
return supportGLES2;
}
/**
* Whether or not this device supports OpenGL ES 3.
* <br/>
* Note that if this returns true, then OpenGL ES 1 and 2 are also supported.
*
* @return true if this device supports OpenGL ES 3; false otherwise.
*/
public boolean supportsGLES3()
{
return supportGLES3;
}
/**
* Gets the underlying {@link EGL10} instance.
*
* @return the underlying {@link EGL10} instance.
*/
public EGL10 getEGL()
{
return mEGL;
}
/**
* Gets the underlying {@link GL10} instance.
*
* @return the underlying {@link GL10} instance.
*/
public GL10 getGL()
{
return mGL;
}
/**
* Gets the underlying {@link EGLDisplay}.
*
* @return the underlying {@link EGLDisplay}
*/
public EGLDisplay getDisplay()
{
return mDisplay;
}
/**
* Gets all supported framebuffer configurations for this device.
*
* @return all supported framebuffer configurations for this device.
*/
public EGLConfig[] getConfigs()
{
return mEGLConfigs;
}
/**
* Gets the underlying {@link EGLContext}.
*
* @return the underlying {@link EGLContext}.
*/
public EGLContext getContext()
{
return mEGLContext;
}
/**
* Gets the underlying {@link EGLSurface}.
*
* @return the underlying {@link EGLSurface}.
*/
public EGLSurface getSurface()
{
return mEGLSurface;
}
// Detects the specific kind of GL modes that are supported
private boolean detect()
{
// Get total number of configs available.
int[] numConfigs = new int[1];
if (!mEGL.eglGetConfigs(mDisplay, null, 0, numConfigs))
{
Log.e("EGLHelper", "Error retrieving number of EGL configs available.");
return false;
}
// Now get all the configurations
mEGLConfigs = new EGLConfig[numConfigs[0]];
if (!mEGL.eglGetConfigs(mDisplay, mEGLConfigs, mEGLConfigs.length, numConfigs))
{
Log.e("EGLHelper", "Error retrieving all EGL configs.");
return false;
}
for (int i = 0; i < mEGLConfigs.length; i++)
{
int[] attribVal = new int[1];
boolean ret = mEGL.eglGetConfigAttrib(mDisplay, mEGLConfigs[i], EGL10.EGL_RENDERABLE_TYPE, attribVal);
if (ret)
{
if ((attribVal[0] & EGL_OPENGL_BIT) != 0)
supportGL = true;
if ((attribVal[0] & EGL_OPENGL_ES2_BIT) != 0)
supportGLES2 = true;
if ((attribVal[0] & EGL_OPENGL_ES3_BIT_KHR) != 0)
supportGLES3 = true;
}
}
return true;
}
// Creates the context and surface.
private void create(int width, int height, int renderableType)
{
int[] attribs = {
EGL10.EGL_WIDTH, width,
EGL10.EGL_HEIGHT, height,
EGL10.EGL_NONE
};
// Initially we just assume GLES2 will be the default context.
int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int[] ctx_attribs = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL10.EGL_NONE
};
// Determine the type of context that will be created
// and change the attribute arrays accordingly.
switch (renderableType)
{
case EGL_OPENGL_ES_BIT:
ctx_attribs[1] = 1;
break;
case EGL_OPENGL_BIT:
ctx_attribs[0] = EGL10.EGL_NONE;
break;
case EGL_OPENGL_ES3_BIT_KHR:
ctx_attribs[1] = 3;
break;
case EGL_OPENGL_ES2_BIT:
default: // Fall-back to GLES 2.
ctx_attribs[1] = 2;
break;
}
mEGLContext = mEGL.eglCreateContext(mDisplay, mEGLConfigs[0], EGL10.EGL_NO_CONTEXT, ctx_attribs);
mEGLSurface = mEGL.eglCreatePbufferSurface(mDisplay, mEGLConfigs[0], attribs);
mEGL.eglMakeCurrent(mDisplay, mEGLSurface, mEGLSurface, mEGLContext);
mGL = (GL10) mEGLContext.getGL();
}
/**
* Simplified call to {@link GL10#glGetString(int)}
* <p>
* Accepts the following constants:
* <ul>
* <li>GL_VENDOR - Company responsible for the GL implementation.</li>
* <li>GL_VERSION - Version or release number.</li>
* <li>GL_RENDERER - Name of the renderer</li>
* <li>GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language </li>
* </ul>
*
* @param glEnum A symbolic constant within {@link GL10}.
*
* @return the string information represented by {@code glEnum}.
*/
public String glGetString(int glEnum)
{
return mGL.glGetString(glEnum);
}
/**
* Simplified call to {@link GLES30#glGetStringi(int, int)}
* <p>
* Accepts the following constants:
* <ul>
* <li>GL_VENDOR - Company responsible for the GL implementation.</li>
* <li>GL_VERSION - Version or release number.</li>
* <li>GL_RENDERER - Name of the renderer</li>
* <li>GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language </li>
* <li>GL_EXTENSIONS - Extension string supported by the implementation at {@code index}.</li>
* </ul>
*
* @param glEnum A symbolic GL constant
* @param index The index of the string to return.
*
* @return the string information represented by {@code glEnum} and {@code index}.
*/
public String glGetStringi(int glEnum, int index)
{
return GLES30.glGetStringi(glEnum, index);
}
/**
* Simplified call to {@link GL10#glGetIntegerv(int, int[], int)
*
* @param glEnum A symbolic GL constant.
*
* @return the integer information represented by {@code glEnum}.
*/
public int glGetInteger(int glEnum)
{
int[] val = new int[1];
mGL.glGetIntegerv(glEnum, val, 0);
return val[0];
}
}